Top > MyPage
 

hibernate入門(+ one-to-one)

Hibernateとは

  • Hibernate( http://www.hibernate.org/ )とは、Gavin King氏を中心とした「Hibernateチーム」が開発しているJavaのオープンソースかつ軽量なO/Rマッピングツール。
  • O/Rマッピングとは、「オブジェクト」と「リレーショナルデータベースの情報」を対応付けするための技術。

    Object指向におけるObjectとRelationalデータベース(RDB)を巧妙に関連付け(マッピング)するためのもの。

    O/Rマッピングツールを利用すると、

  • 1.単調で複雑な作業から開放される。
  • 2.自動化されることによってバグが入り込む隙が無くなる

Hibernateを利用することの利点

コーディング量が減る
コネクションの取得に関する記述、発行するSQL文に関する記述、結果セットからのオブジェクト(JavaBean)構築に関する記述が不要に。
パーシステンス(オブジェクトの取得や保存)サービス層の分離
※パーシステンス(オブジェクトやシステムの状態などを、プロセスやセッション、マシンなどの物理的、時間的境界を越えて維持、保存する機能)
レイジーローディング機能、キャッシュ機能などをサポートしている
※レイジーローディング(必要になるまで関連するクラスのインスタンスを取得しない)
特定のDBMSへの依存度の軽減
コンテナを必要とせず、パーシステンスに関連する機能だけを提供してくれる

one-to-one

one-to-one(1対1)関係について

  • one-to-oneとは同一の主キーを持つテーブルの関係を表す。

    「1対1」の関連をマッピングする場合に利用する。派生関係ともいう。

    関連元の主キーと関連先の主キーが同じものを関連にする。

    ※主キー(Primary Key)とはテーブルの中のひとつの行を特定するために使われるもの。

    ※主キーが複数のフィールドを含む場合、複合キーと呼ばれる

今回はこのように作成した。

マッピングは次のように行う。

Sample.hbm.xml

 1: <?xml version="1.0" encoding="Windows-31J"?>
 2: <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3:  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4: 
 5: <hibernate-mapping>
 6:   <class table="users" name="com.chikkun.hibernate.hoge.fuga">
 7:     <id name="userId" column="user_id">
 8:       <generator class="sequence">
 9:         <param name="sequence">users_user_id_seq</param>
10:       </generator>
11:     </id>
12:     
13:     <one-to-one
14:         name="propertyName"
15:         class="ClassName"
16:         cascade="cascade_style"
17:         constrained="true|false"
18:         fetch="join|select"
19:         property-ref="propertyNameFromAssociatedClass"
20:         access="field|property|ClassName"
21:         formula="any SQL expression"
22:         lazy="proxy|no-proxy|false"
23:         entity-name="EntityName"
24:         node="element-name|@attribute-name|element/@attribute|."
25:         embed-xml="true|false"
26:         foreign-key="foreign_key_name"
27:     />
28: 
29:     <property name="userName" column="user_name" not-null="true"/>
30:     <property name="userPassword" column="userpassword" not-null="true"/>
31:   </class>
32: </hibernate-mapping>
name
プロパティ名。
class
(オプション - デフォルトはリフレクションにより決定されるプロパティの型):関連クラスの名前。
generator

主キーとなるプロパティの値を発生させる方法を指定するための要素。

自動的に値を取得してくれるものと、自分で値を登録するものに分かれる。

identity:DB2, MySQL, MS SQL Server, Sybase, HypersonicSQLのアイデンティティ・カラムを サポート。

sequence:DB2, PostgreSQL, Oracle, SAP DB, McKoiのシーケンス、 またはInterbaseのジェネレータを使う。

hilo:hi/loアルゴリズムを使用してIDを生成する。

assigned:アプリケーションに識別子を割り当てさせたい場合に使用する。オブジェクトの識別子プロパティに

既に割り当てられている識別子を使う。このため、このジェネレータを利用しているエンティティは、セッション

のsaveOrUpdate()メソッドによって保存することができない。save()もしくはupdate()を明示的に呼び出す必要あり。

cascade

(オプション - 親オブジェクトから関連オブジェクトへ、どの操作をカスケード(伝播)するかを指定する。

all,none,save-update,deleteを指定できる。

allは更新と削除の両方、save-updateは更新、deleteは削除を伝播させる。)

outer-join

true:関連するオブジェクトを取得するのに、外部結合による問い合わせが実行される。

false:関連するオブジェクトを取得するのに、外部結合を使わずに、それぞれの問い合わせが実行される。

auto:Hibernateの設定による外部結合を使うかを自動的に判定する。

この場合、hibernate.cfg.xmlやhibernate.propertiesに次のような設定が必要になる。

<property name="user_outer_join">true</property>※もしくはfalse

constrained

(オプション):マッピングされたテーブルの主キーに対する外部キー制約が、関連クラスのテーブルを

参照することを指定する。このオプションはsave()とdelete()がカスケードされる順序に影響し、そして

関連がプロキシされるかどうかにも影響する。



            
one-to-oneタグの中の設定

	
	            name
:プロパティ名
		
	            class
(オプション):関連クラスの名前
		
	            cascade
(オプション):親オブジェクトから関連オブジェクトへ、どの操作を伝播させるか指定する。
		
	            constrained
(オプション):マッピングされたテーブルの主キーに対する外部キー制約が、
		関連クラスのテーブルを参照することを指定する。このオプションはsave()とdelete()が
		カスケードされる順序に影響し、関連がプロキシされるかどうかにも影響する。
		
	            fetch
(オプション-デフォルトはselect):外部結合フェッチ(join)と順次選択フェッチ(sequential select fetch)
		のどちらかを選択する。
		・select:外部結合を行わない。別々のSELECT文を発行して関連しあうオブジェクトを取得
		・join:LAZYロードせずに、外部結合によるDB問い合わせを行う。
	            
              参考
Hibernate マッピング - fetch 戦略でパフォーマンス改善 | 概要            

			
	            property-ref
(オプション):このクラスの主キーに結合された関連クラスのプロパティ名。
		指定されなければ、関連クラスの主キーが使われる。

	            access
(オプション-デフォルトはproperty):Hibernateがプロパティの値にアクセスするために使用すべき戦略。
	
	            formula
(オプション):ほとんど全ての一対一関連はオーナーのエンティティの主キーへとマッピングされる。
		これ以外の場合、他のカラム、複数のカラム、SQL構文を使った結合するための式を指定できる。

	            lazy
(オプション-デフォルトはproxy):デフォルトでは、多重度1の関連がプロキシとなる。
		lazy="no-proxy"は、インスタンス変数に最初にアクセスした時に、プロパティを遅延フェッチするよう指定。
		lazy="false"は、関連を常に即時にフェッチするよう指定。

	
	            entity-name
(オプション):関連したクラスのエンティティ名。
          

DTOの作成

DBはusersテーブル(ログインユーザテーブル)とuser_idテーブル(ユーザの属性をもつ)を作成するので、そのための準備をする。

uersテーブル用のDTO

Users.java

  1: package com.chikkun.hibernate.sato;
  2: 
  3: import java.io.Serializable;
  4: import java.util.Set;
  5: 
  6: public class Users implements Serializable {
  7:     
  8:     /**
  9:      * 
 10:      */
 11:     private static final long serialVersionUID = 5627186958621777118L;
 12: 
 13:     /**
 14:      * userId
 15:      */
 16:     private Integer userId;
 17:     
 18:     /**
 19:      * userName
 20:      */
 21:     private String userName;
 22:     
 23:     /**
 24:      * userPassword
 25:      */
 26:     private String userPassword;
 27:     
 28:     /**
 29:      * user_info_id
 30:      */
 31:     private Integer userInfoId;
 32:     
 33:     /**
 34:      * usersInfo
 35:      */
 36:     private UsersInfo usersInfo;
 37:     
 38:     /**
 39:      * purchaseHistory
 40:      */
 41:     private Set<Object> purchaseHistory;
 42: 
 43:     public Users() {
 44:         super();
 45:     }
 46:     
 47:     /**
 48:      * @return the userId
 49:      */
 50:     public Integer getUserId() {
 51:         return userId;
 52:     }
 53: 
 54:     /**
 55:      * @param userId the userId to set
 56:      */
 57:     public void setUserId(Integer userId) {
 58:         this.userId = userId;
 59:     }
 60: 
 61:     /**
 62:      * @return the userName
 63:      */
 64:     public String getUserName() {
 65:         return userName;
 66:     }
 67: 
 68:     /**
 69:      * @param userName the userName to set
 70:      */
 71:     public void setUserName(String userName) {
 72:         this.userName = userName;
 73:     }
 74: 
 75:     /**
 76:      * @return the userPassword
 77:      */
 78:     public String getUserPassword() {
 79:         return userPassword;
 80:     }
 81: 
 82:     /**
 83:      * @param userPassword the userPassword to set
 84:      */
 85:     public void setUserPassword(String userPassword) {
 86:         this.userPassword = userPassword;
 87:     }
 88: 
 89:     /**
 90:      * @return the userInfoId
 91:      */
 92:     public Integer getUserInfoId() {
 93:         return userInfoId;
 94:     }
 95: 
 96:     /**
 97:      * @param userInfoId the userInfoId to set
 98:      */
 99:     public void setUserInfoId(Integer userInfoId) {
100:         this.userInfoId = userInfoId;
101:     }
102: 
103:     /**
104:      * @return the userInfo
105:      */
106:     public UsersInfo getUsersInfo() {
107:         return usersInfo;
108:     }
109: 
110:     /**
111:      * @param usersInfo the userInfo to set
112:      */
113:     public void setUsersInfo(UsersInfo usersInfo) {
114:         this.usersInfo = usersInfo;
115:     }
116: 
117:     /**
118:      * @return the purchaseHistory
119:      */
120:     public Set<Object> getPurchaseHistory() {
121:         return purchaseHistory;
122:     }
123: 
124:     /**
125:      * @param purchaseHistory the purchaseHistory to set
126:      */
127:     public void setPurchaseHistory(Set<Object> purchaseHistory) {
128:         this.purchaseHistory = purchaseHistory;
129:     }
130: 
131:     public String toString(){
132:         return "user_id[" + userId + "] " + "user_name[" + userName + "] " +
133:             "userPassword[" + userPassword + "] " + "user_info_id[" + userInfoId + "]";
134:     }
135: }

users.hbm.xmlはこのように作成した。

users.hbm.xml

 1: <?xml version="1.0" encoding="Windows-31J"?>
 2: <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3:  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4: 
 5: <hibernate-mapping>
 6:   <class table="users" name="com.chikkun.hibernate.sato.Users">
 7:     <id name="userId" column="user_id">
 8:       <generator class="sequence">
 9:         <param name="sequence">users_user_id_seq</param>
10:       </generator>
11:     </id>
12: <one-to-one name="userInfo" class="com.chikkun.hibernate.sato.UsersInfo"/>
13:     <property name="userName" column="user_name" not-null="true"/>
14:     <property name="userPassword" column="userpassword" not-null="true"/>
15:   </class>
16: </hibernate-mapping>

one-to-oneにするために

[<source></source> is illegal in <p>]

と記述している。

user_infoテーブルのDTO

UsersInfo.java

  1: package com.chikkun.hibernate.sato;
  2: 
  3: import java.io.Serializable;
  4: 
  5: public class UsersInfo implements Serializable {
  6: 
  7:     /**
  8:      * 
  9:      */
 10:     private static final long serialVersionUID = 1893088480400694912L;
 11: 
 12:     /**
 13:      * userInfoId
 14:      */
 15:     private Integer userInfoId;
 16:     
 17:     /**
 18:      * userId
 19:      */
 20:     
 21:     /**
 22:      * age
 23:      */
 24:     private Integer age;
 25:     
 26:     /**
 27:      * sex
 28:      */
 29:     private Integer sex;
 30:     
 31:     /**
 32:      * favorite
 33:      */
 34:     private String favorite;
 35:     
 36:     /**
 37:      * users
 38:      */
 39:     private Users users;
 40:     
 41:     public UsersInfo() {
 42:         super();
 43:     }
 44: 
 45:     /**
 46:      * @return the userInfoId
 47:      */
 48:     public Integer getUserInfoId() {
 49:         return userInfoId;
 50:     }
 51: 
 52:     /**
 53:      * @param userInfoId the userInfoId to set
 54:      */
 55:     public void setUserInfoId(Integer userInfoId) {
 56:         this.userInfoId = userInfoId;
 57:     }
 58: 
 59:     /**
 60:      * @return the age
 61:      */
 62:     public Integer getAge() {
 63:         return age;
 64:     }
 65: 
 66:     /**
 67:      * @param age the age to set
 68:      */
 69:     public void setAge(Integer age) {
 70:         this.age = age;
 71:     }
 72: 
 73:     /**
 74:      * @return the sex
 75:      */
 76:     public Integer getSex() {
 77:         return sex;
 78:     }
 79: 
 80:     /**
 81:      * @param sex the sex to set
 82:      */
 83:     public void setSex(Integer sex) {
 84:         this.sex = sex;
 85:     }
 86: 
 87:     /**
 88:      * @return the favorite
 89:      */
 90:     public String getFavorite() {
 91:         return favorite;
 92:     }
 93: 
 94:     /**
 95:      * @param favorite the favorite to set
 96:      */
 97:     public void setFavorite(String favorite) {
 98:         this.favorite = favorite;
 99:     }
100: 
101:     /**
102:      * @return the users
103:      */
104:     public Users getUsers() {
105:         return users;
106:     }
107: 
108:     /**
109:      * @param users the users to set
110:      */
111:     public void setUsers(Users users) {
112:         this.users = users;
113:     }
114: 
115:     public String toString() {
116:         return "userInfoId[" + userInfoId + "] " + "age[" + age + "] " + 
117:             "sex[" + sex + "] " + "favorite[" + favorite + "] ";
118:     }
119: 
120: }

UsersInfoクラスのhbmファイルは以下のようになる。

UsersInfo.hbm.xml

 1: <?xml version="1.0" encoding="Windows-31J"?>
 2: <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
 3:  "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 4: 
 5: <hibernate-mapping>
 6:   <class table="user_info" name="com.chikkun.hibernate.sato.UsersInfo">
 7:     <id unsaved-value="null" name="userInfoId" column="user_info_id">
 8:       <generator class="foreign">
 9:         <param name="property">users</param>
10:       </generator>
11:     </id>
12: <one-to-one name="users" constrained="true" class="com.chikkun.hibernate.sato.Users" cascade="all"/>
13:     <property name="age" column="age" not-null="false"/>
14:     <property name="sex" column="sex" not-null="false"/>
15:     <property name="favorite" column="favorite" not-null="false"/>
16:   </class>
17: </hibernate-mapping>

こちらでも、

<one-to-one name="users" constrained="true" class="com.chikkun.hibernate.sato.Users" cascade="all"/>

と記述している。cascade="all"を設定しておく。

hibernateの設定ファイルの作成

hibernate.cfg.xmlは次のように作成した。

hibernate.cfg.xml

 1: <!DOCTYPE hibernate-configuration
 2:     PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
 3: 
 4: <hibernate-configuration>
 5: 
 6:     <session-factory>
 7:         <property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
 8:         <property name="show_sql">true</property>
 9:         <property name="connection.driver_class">org.postgresql.Driver</property>
10:         <property name="connection.url">jdbc:postgresql://localhost:5432/hibernate_test</property>
11:         <property name="hibernate.connection.username">chikkun</property>
12:         <property name="hibernate.connection.password">kazukun</property>
13:         
14:         <!-- Mapping files -->
15:             <mapping resource="com/chikkun/hibernate/sato/Users.hbm.xml"/>
16:             <mapping resource="com/chikkun/hibernate/sato/UsersInfo.hbm.xml"/>
17:     </session-factory>
18: 
19: </hibernate-configuration>

DAOの作成

まずはsessionの管理を行う親クラスを作成する。

DaoSupport.java

 1: package com.chikkun.hibernate.sato.database.dao;
 2: 
 3: import org.hibernate.HibernateException;
 4: import org.hibernate.Session;
 5: import org.hibernate.SessionFactory;
 6: import org.hibernate.cfg.Configuration;
 7: 
 8: public abstract class DaoSupport {
 9:     
10:     private static SessionFactory sessionFactory = null;
11:     
12:     static synchronized SessionFactory getSessionFactory() throws HibernateException {
13:         
14:         if (sessionFactory == null) {
15:             Configuration cfg = new Configuration();
16:             cfg.configure("hibernate.cfg.xml");
17:             sessionFactory = cfg.buildSessionFactory();
18:         }
19:         return sessionFactory;
20:     }
21:     
22:     public Session getSession() throws HibernateException {
23: 
24:         SessionFactory sf = getSessionFactory();
25:         return sf.openSession();
26:     }
27:     
28:     public void closeSession(Session session) {
29:         if (session != null) {
30:             try {
31:                 session.close();
32:             } catch (HibernateException ignore) {}
33:         }
34:     }
35: }

DaoSupportクラスを継承した、UsersクラスのDAOを作成。

UsersDAO.java

  1: package com.chikkun.hibernate.sato.database.dao;
  2: 
  3: import java.util.List;
  4: 
  5: import org.hibernate.HibernateException;
  6: import org.hibernate.Query;
  7: import org.hibernate.Session;
  8: import org.hibernate.Transaction;
  9: 
 10: import com.chikkun.hibernate.sato.Users;
 11: 
 12: public class UsersDAO extends DaoSupport{
 13:     
 14:     public void saveUsers(Users users){
 15:         
 16:         Session session = null;
 17:         Transaction transaction = null;
 18:         
 19:         try{
 20:             session = getSession();
 21:             transaction = session.beginTransaction();
 22:             session.save(users);
 23:             transaction.commit();
 24:         } catch (HibernateException e){
 25:             if(transaction != null){
 26:                 try{
 27:                     transaction.rollback();
 28:                 } catch (HibernateException ignore){
 29:                     
 30:                 }
 31:             }
 32:             throw new RuntimeException(e.getMessage());
 33:         } finally {
 34:             if(session != null) {
 35:                 try {
 36:                     session.close();
 37:                 } catch (HibernateException ignore){
 38:                     
 39:                 }
 40:             }
 41:         }
 42:         
 43:     }
 44:     
 45:     public final void updateUsers(final Users _staff) {
 46:         
 47:         Session session = null;
 48:         Transaction transaction = null;
 49:         
 50:         try {
 51:             session = getSession();
 52:             transaction = session.beginTransaction();
 53:             session.update(_staff);
 54:             transaction.commit();
 55:         } catch (HibernateException e) {
 56:             if (transaction != null) {
 57:                 try {
 58:                     transaction.rollback();
 59:                 } catch (HibernateException ignore){
 60:                     
 61:                 }
 62:             }
 63:             throw new RuntimeException(e.getMessage());
 64:         } finally {
 65:             if (session != null){
 66:                 try{
 67:                     session.close();
 68:                 } catch (HibernateException ignore){
 69:                     
 70:                 }
 71:             }
 72:         }
 73:     }
 74:     
 75:     public final void deleteUsers(final Users _staff) {
 76:         Session session = null;
 77:         Transaction transaction = null;
 78:         try {
 79:             session = getSession();
 80:             transaction = session.beginTransaction();
 81:             session.delete(_staff);
 82:             transaction.commit();
 83:         } catch (HibernateException ex){
 84:             if(transaction != null){
 85:                 try {
 86:                     transaction.rollback();
 87:                 } catch (HibernateException ignore) {
 88:                     
 89:                 }
 90:             }
 91:             throw new RuntimeException(ex.getMessage());
 92:         } finally {
 93:             if (session != null) {
 94:                 try {
 95:                     session.close();
 96:                 } catch (HibernateException ignore) {
 97:                     
 98:                 }
 99:             }
100:         }
101:     }
102:     
103:     public final Users findByUserId(final Integer userId) {
104:         Session session = getSession();
105:         Query query = session.createQuery("from Users as users where users.userId = " + userId);
106:         List list = query.list();
107:         if(list.size() > 0){
108:             session.close();
109:             return (Users) list.get(0);
110:         }
111:         session.close();
112:         return null;
113:     }
114:     
115:     public final void delete(final Users users) throws Exception {
116:         Session sess = this.getSession();
117:         Transaction ts = null;
118:         try {
119:             ts = sess.beginTransaction();
120:             sess.delete(users);
121:             sess.flush();
122:             ts.commit();
123:         } catch (Exception e){
124:             if (ts != null) ts.rollback();
125:             e.printStackTrace();
126:             throw e;
127:         } finally {
128:             closeSession(sess);
129:         }
130:     }
131: }

UsersInfoクラスのDAOも作成。

UsersInfoDAO.java

  1: package com.chikkun.hibernate.sato.database.dao;
  2: 
  3: import java.util.List;
  4: 
  5: import org.hibernate.HibernateException;
  6: import org.hibernate.Query;
  7: import org.hibernate.Session;
  8: import org.hibernate.Transaction;
  9: 
 10: import com.chikkun.hibernate.sato.UsersInfo;
 11: 
 12: public class UsersInfoDAO extends DaoSupport{
 13:     
 14:     
 15:     public void saveUsersInfo(UsersInfo userInfo) {
 16:         
 17:         Session session = null;
 18:         Transaction transaction = null;
 19:         
 20:         try {
 21:             session = getSession();
 22:             transaction = session.beginTransaction();
 23:             session.save(userInfo);
 24:             transaction.commit();
 25:         } catch (HibernateException e) {
 26:             if (transaction != null){
 27:                 try {
 28:                     transaction.rollback();
 29:                 } catch (HibernateException ignore) {
 30:                     
 31:                 }
 32:             }
 33:             throw new RuntimeException(e.getMessage());
 34:         } finally {
 35:             if (session != null){
 36:                 try {
 37:                     session.close();
 38:                 } catch (HibernateException ignore) {
 39:                     
 40:                 }
 41:             }
 42:         }
 43:     }
 44:     
 45:     public final void updateUsersInfo(final UsersInfo _staff) {
 46:         Session session = null;
 47:         Transaction transaction = null;
 48:         try {
 49:             session = getSession();
 50:             transaction = session.beginTransaction();
 51:             session.update(_staff);
 52:             transaction.commit();
 53:         } catch (HibernateException e){
 54:             if (transaction != null) {
 55:                 try {
 56:                     transaction.rollback();
 57:                 } catch (HibernateException ignore) {
 58:                     
 59:                 }
 60:             }
 61:             throw new RuntimeException(e.getMessage());
 62:         } finally {
 63:             if (session != null){
 64:                 try{
 65:                     session.close();
 66:                 } catch (HibernateException ignore){
 67:                     
 68:                 }
 69:             }
 70:         }
 71:     }
 72:     
 73:     public final void deleteUsersInfo(final UsersInfo userInfo){
 74:         Session session = null;
 75:         Transaction transaction = null;
 76:         try {
 77:             session = getSession();
 78:             transaction = session.beginTransaction();
 79:             session.delete(userInfo);
 80:             transaction.commit();
 81:         } catch (HibernateException ex){
 82:             if(transaction != null) {
 83:                 try {
 84:                     transaction.rollback();
 85:                 } catch (HibernateException ignore) {
 86:                     
 87:                 }
 88:             }
 89:             throw new RuntimeException(ex.getMessage());
 90:         } finally {
 91:             if (session != null) {
 92:                 try {
 93:                     session.close();
 94:                 } catch (HibernateException ignore) {
 95:                     
 96:                 }
 97:             }
 98:         }
 99:     }
100:     
101:     public final UsersInfo findByUsersInfoId(final Integer id) {
102:         Session session = getSession();
103:         Query query = session.createQuery("from UsersInfo as usersInfo where usersInfo.userInfoId = " + id);
104:         List list = query.list();
105:         if(list.size() > 0){
106:             session.close();
107:             return (UsersInfo) list.get(0);
108:         }
109:         session.close();
110:         return null;
111:     }
112:     
113:     public final void delete(final UsersInfo usersInfo) throws Exception {
114:         Session sess = this.getSession();
115:         Transaction ts = null;
116:         try {
117:             ts = sess.beginTransaction();
118:             sess.delete(usersInfo);
119:             sess.flush();
120:             ts.commit();
121:         } catch (Exception e){
122:             if (ts != null) ts.rollback();
123:             e.printStackTrace();
124:             throw e;
125:         } finally {
126:             closeSession(sess);
127:         }
128:     }
129: }

DBの作成

今回はシンプルにこのように作成する。

DB:hibernate_test

CREATE TABLE users
(
  user_id serial NOT NULL,
  user_name character varying(64) NOT NULL,
  userpassword character varying(64) NOT NULL,
  CONSTRAINT users_pkey PRIMARY KEY (user_id)
)

CREATE TABLE user_info
(
  user_info_id integer NOT NULL,
  age numeric(3),
  sex numeric(1),
  favorite character varying(40),
  CONSTRAINT user_info_pkey PRIMARY KEY (user_info_id),
  CONSTRAINT fk_user_info_1 FOREIGN KEY (user_info_id)
      REFERENCES users (user_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

Antで実行

今回はメインメソッドから実行することにする。

Mainメソッドは以下の通り

Main(UsersInfo側をアップデート)

 1: package com.chikkun.hibernate.sato.main;
 2: 
 3: import com.chikkun.hibernate.sato.Users;
 4: import com.chikkun.hibernate.sato.UsersInfo;
 5: import com.chikkun.hibernate.sato.database.dao.UsersInfoDAO;
 6: 
 7: 
 8: /**
 9:  * Main
10:  * @author sato
11:  *
12:  */
13: public class Main {
14: 
15:     public static void main(String[] args) throws Exception {
16:         
17:         UsersInfoDAO udao = new UsersInfoDAO();
18:         
19:         Users users = new Users();
20:         
21:         UsersInfo usersInfo = new UsersInfo();
22:         
23:         usersInfo.setAge(17);
24:         usersInfo.setFavorite("music");
25:         usersInfo.setSex(1);
26:         
27:         users.setUserName("hoge");
28:         users.setUserPassword("fuga");
29:         users.setUserInfo(usersInfo);
30:         usersInfo.setUsers(users);
31:         
32:         //このケースではUsersInfoをアップデートしてみる
33:         udao.saveUsersInfo(usersInfo);
34:     }
35: }

Antで実行するために、build.xmlを次のように作成した。

build.xml

 1: <?xml version="1.0" encoding="Windows-31J"?>
 2: <?xml version="1.0" encoding="Windows-31J"?>
 3: <project name="hibernate_sato-from-maven" default="hibernateTest1">
 4:     <import file="maven-build.xml"/>
 5: 
 6:     <!-- define library filesets. -->
 7:     <fileset id="hibernate.libs" dir="lib/hibernate-distribution-3.3.1.GA">
 8:         <include name="*.jar"/>
 9:         <include name="lib/required/*.jar"/>
10:     </fileset>
11: 
12:     <fileset id="slf4j.libs" dir="lib/slf4j-1.5.2">
13:         <include name="*.jar"/>
14:     </fileset>
15: 
16:     <fileset id="log4j.libs" dir="lib/apache-log4j-1.2.15">
17:         <include name="*.jar"/>
18:     </fileset>
19: 
20:     <fileset id="xerces.libs" dir="lib/xerces-2_9_1">
21:         <include name="*.jar"/>
22:     </fileset>
23: 
24:     <fileset id="psql.libs" dir="lib">
25:         <include name="postgresql-8.3-603.jdbc4.jar"/>
26:     </fileset>
27: 
28:     <!-- define build classpath. -->
29:     <path id="build.classpath">
30:         <pathelement path="target/classes"/>
31:         <fileset refid="hibernate.libs"/>
32:         <fileset refid="slf4j.libs"/>
33:         <fileset refid="log4j.libs"/>
34:         <fileset refid="xerces.libs"/>
35:         <fileset refid="psql.libs"/>
36:     </path>
37: 
38:     <target name="compile" description="コンパイル">
39:         <javac srcdir="src" destdir="target/classes" debug="on" 
40:             encoding="Windows-31J" deprecation="on" optimize="off">
41:             <classpath refid="build.classpath"/>
42:         </javac>
43:         <copy todir="target/classes" filtering="no" overwrite="yes">
44:             <fileset dir="src">
45:                 <include name="**/*.hbm.xml"/>
46:             </fileset>
47:         </copy>
48:     </target>
49: 
50:     <target name="hibernateTest1" description="hibernateTest1" depends="compile">
51:         <java classname="com.chikkun.hibernate.sato.main.Main">
52:             <classpath refid="build.classpath"/>
53:         </java>
54:     </target>
55: </project>

Eclipse上でAntを実行すると

[<figure src="" alt=""></figure> is illegal in <p>]

[<figure src="" alt=""></figure> is illegal in <p>]

のように、usersテーブル、user_infoテーブルに保存が行われる。

Users側を保存するとどうなるか

今度はMainメソッドを少し変更して、Usersの方を保存してみる

Main2.java

 1:     public static void main(String[] args) throws Exception {
 2:         
 3:         UsersDAO udao = new UsersDAO();
 4:         
 5:         Users users = new Users();
 6:         
 7:         UsersInfo usersInfo = new UsersInfo();
 8:         
 9:         usersInfo.setAge(80);
10:         usersInfo.setFavorite("baseBall");
11:         usersInfo.setSex(2);
12:         
13:         users.setUserName("wawawa");
14:         users.setUserPassword("wawawa");
15:         users.setUserInfo(usersInfo);
16:         usersInfo.setUsers(users);
17:         
18:         udao.saveUsers(users);
19:     }

コンソールにはこのように出力される。

    [javac] Compiling 6 source files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
     [copy] Copying 2 files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
hibernateTest1:
     [java] log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
     [java] log4j:WARN Please initialize the log4j system properly.
     [java] Hibernate: select nextval ('users_user_id_seq')
     [java] Hibernate: insert into users (user_name, userpassword, user_id) values (?, ?, ?)

Users側をセーブした結果は、

[<figure src="" alt=""></figure> is illegal in <p>]

[<figure src="" alt=""></figure> is illegal in <p>]

のように、usersテーブルには保存されているが、users,user_infoテーブルに保存は行われていない。

これは、UsersInfo.hbm.xmlには

<one-to-one name="users" constrained="true" class="com.chikkun.hibernate.sato.Users" cascade="all"/>

cascade="all"を指定しているが、Users.hbm.xmlの方にはcascadeを設定していないから。

Users.hbm.xmlの方にのみcascade="all"を設定し、もう一度Main2クラスのメインメソッドを実行すると、

[<figure src="" alt=""></figure> is illegal in <p>]

[<figure src="" alt=""></figure> is illegal in <p>]

    [javac] Compiling 6 source files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
     [copy] Copying 2 files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
hibernateTest1:
     [java] log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
     [java] log4j:WARN Please initialize the log4j system properly.
     [java] Hibernate: select nextval ('users_user_id_seq')
     [java] Hibernate: insert into users (user_name, userpassword, user_id) values (?, ?, ?)
     [java] Hibernate: insert into user_info (age, sex, favorite, user_info_id) values (?, ?, ?, ?)

users,users_infoの両テーブルに値が保存された。

Delete

まずはUsers側からDelete

 1:     //Delete
 2:     public static void main(String[] args) {
 3:         
 4:         SessionManager.initialize();
 5:         Integer userId = 7;
 6:             
 7:         UsersDAO udao = new UsersDAO();
 8:         
 9:         try{
10:             Users users = udao.findByUserId(userId);
11:             System.out.println("\n============================================\n"
12:                     + users.toString());
13:             
14:             if(users != null && users instanceof Users){
15:                 udao.delete(users);
16:                 System.out.println("\n----------------------------------------\ndelete complete:"
17:                         + users.getUserName());
18:             }
19:         } catch (Exception e){
20:             e.printStackTrace();
21:         }
22:     }

を実行し、Users側からのDeleteを試みる。

結果は、

 1:     [javac] Compiling 9 source files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
 2:      [copy] Copying 2 files to C:\Cygwin\home\chikkun\workspace\hibernate_sato\target\classes
 3: hibernateTest1:
 4:      [java] log4j:WARN No appenders could be found for logger (org.hibernate.cfg.Environment).
 5:      [java] log4j:WARN Please initialize the log4j system properly.
 6:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 7:       as userpass3_4_ from users users0_ where users0_.user_id=7
 8:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_0_, usersinfo0_.age as age5_0_, usersinfo0_.sex
 9:       as sex5_0_, usersinfo0_.favorite as favorite5_0_ from user_info usersinfo0_ where usersinfo0_.user_info_id=?
10:      [java] ============================================
11:      [java] user_id[7] user_name[wawawa] userPassword[wawawa] 
12:      [java] Hibernate: delete from users where user_id=?
13:      [java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
14:      [java] 	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
15:      [java] 	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
16:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
17:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
18:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
19:      [java] 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
20:      [java] 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
21:      [java] 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
22:      [java] 	at com.chikkun.hibernate.sato.database.dao.UsersDAO.delete(UsersDAO.java:121)
23:      [java] 	at com.chikkun.hibernate.sato.main.Main3.main(Main3.java:32)
24:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
25:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
26:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
27:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
28:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
29:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
30:      [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:747)
31:      [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:201)
32:      [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:104)
33:      [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
34:      [java] 	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
35:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
36:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
37:      [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
38:      [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
39:      [java] 	at org.apache.tools.ant.Target.execute(Target.java:357)
40:      [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:385)
41:      [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
42:      [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
43:      [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
44:      [java] 	at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
45:      [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
46:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
47:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)
48:      [java] Caused by: java.sql.BatchUpdateException: Batch entry 0 delete from users where user_id=7 was aborted.  Call getNextException to see the cause.
49:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
50:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
51:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
52:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
53:      [java] 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
54:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
55:      [java] 	... 31 more

ConstraintViolationExceptionが発生し、Deleteできず。

今度はUsersInfo側からDelete

UsersInfo側からdeleteを行う(user_info_id = 7のデータをdelete)と

 1:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_, usersinfo0_.age as age5_, usersinfo0_.sex
 2:       as sex5_, usersinfo0_.favorite as favorite5_ from user_info usersinfo0_ where usersinfo0_.user_info_id=7
 3:      [java] ============================================
 4:      [java] userInfoId[7] age[80] sex[2] favorite[baseBall] 
 5:      [java] Hibernate: select users0_.user_id as user1_4_1_, users0_.user_name as user2_4_1_, users0_.userpassword
 6:       as userpass3_4_1_, usersinfo1_.user_info_id as user1_5_0_, usersinfo1_.age as age5_0_, usersinfo1_.sex
 7:        as sex5_0_, usersinfo1_.favorite as favorite5_0_ from users users0_ left outer join user_info usersinfo1_
 8:         on users0_.user_id=usersinfo1_.user_info_id where users0_.user_id=?
 9:      [java] Hibernate: delete from user_info where user_info_id=?
10:      [java] Hibernate: delete from users where user_id=?
11:      [java] ----------------------------------------
12:      [java] delete complete:userInfoId[7] age[80] sex[2] favorite[baseBall] 

無事Deleteを行うことができた。

Delete前

[<figure src="" alt=""></figure> is illegal in <p>]

[<figure src="" alt=""></figure> is illegal in <p>]

Delete後

[<figure src="" alt=""></figure> is illegal in <p>]

[<figure src="" alt=""></figure> is illegal in <p>]

では何故users側からdeleteが行えないのか

users側から削除できないのはどうしてかを調べてみた。

  1. UserInfo.hbm.xmlのone-to-oneタグで constrained="true" を設定しているから削除できない
  2. UserInfo.hbm側がcascade=allで、Users.hbm側でcascadeを設定していないから削除できない
  3. 上記が原因ではなく、DBのCONSTRAINTのせいで削除できない

検証その1

現在UserInfo.hbmは

UsersInfo.hbm.xml

 1: <hibernate-mapping>
 2:   <class table="user_info" name="com.chikkun.hibernate.sato.UsersInfo">
 3:     <id unsaved-value="null" name="userInfoId" column="user_info_id">
 4:       <generator class="foreign">
 5:         <param name="property">users</param>
 6:       </generator>
 7:     </id>
 8: <one-to-one name="users" constrained="true" class="com.chikkun.hibernate.sato.Users" cascade="all"/>
 9:     <property name="age" column="age" not-null="false"/>
10:     <property name="sex" column="sex" not-null="false"/>
11:     <property name="favorite" column="favorite" not-null="false"/>
12:   </class>
13: </hibernate-mapping>

という設定にしている。このconstrained="true"の部分を

<one-to-one name="users" constrained="false" class="com.chikkun.hibernate.sato.Users" cascade="all"/>

と変更し、再びUsers側からの削除を行ってみる。

userId=8のデータに対しdeleteを行う。

結果は

constrained="true"の時と同様

が発生し、deleteが行えなかった。

 1:     [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 2:      as userpass3_4_ from users users0_ where users0_.user_id=8
 3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_2_, usersinfo0_.age as age5_2_, usersinfo0_.sex
 4:       as sex5_2_, usersinfo0_.favorite as favorite5_2_, users1_.user_id as user1_4_0_, users1_.user_name
 5:        as user2_4_0_, users1_.userpassword as userpass3_4_0_, usersinfo2_.user_info_id as user1_5_1_, usersinfo2_.age
 6:         as age5_1_, usersinfo2_.sex as sex5_1_, usersinfo2_.favorite as favorite5_1_ from user_info usersinfo0_
 7:          left outer join users users1_ on usersinfo0_.user_info_id=users1_.user_id
 8:           left outer join user_info usersinfo2_ on users1_.user_id=usersinfo2_.user_info_id
 9:            where usersinfo0_.user_info_id=?
10:      [java] ============================================
11:      [java] user_id[8] user_name[aaa] userPassword[aaa] 
12:      [java] Hibernate: delete from users where user_id=?
13:      [java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
14:      [java] 	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
15:      [java] 	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
16:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
17:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:266)
18:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
19:      [java] 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
20:      [java] 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
21:      [java] 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
22:      [java] 	at com.chikkun.hibernate.sato.database.dao.UsersDAO.delete(UsersDAO.java:121)
23:      [java] 	at com.chikkun.hibernate.sato.main.Main3.main(Main3.java:34)
24:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
25:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
26:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
27:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
28:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
29:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
30:      [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:747)
31:      [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:201)
32:      [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:104)
33:      [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
34:      [java] 	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
35:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
36:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
37:      [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
38:      [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
39:      [java] 	at org.apache.tools.ant.Target.execute(Target.java:357)
40:      [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:385)
41:      [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
42:      [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
43:      [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
44:      [java] 	at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
45:      [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
46:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
47:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)
48:      [java] Caused by: java.sql.BatchUpdateException: Batch entry 0 delete from users where user_id=8 was aborted.  Call getNextException to see the cause.
49:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
50:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
51:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
52:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
53:      [java] 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
54:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
55:      [java] 	... 31 more

検証その2

今度はUsers.hbmにcascade="all"を記述してみる。UsersInfo.hbmはcascadeの部分を削除し、constrained="false"はそのままにしておく。

users.hbm.xml

 1: <hibernate-mapping>
 2:   <class table="users" name="com.chikkun.hibernate.sato.Users">
 3:     <id name="userId" column="user_id">
 4:       <generator class="sequence">
 5:         <param name="sequence">users_user_id_seq</param>
 6:       </generator>
 7:     </id>
 8: <one-to-one name="userInfo" class="com.chikkun.hibernate.sato.UsersInfo" cascade="all"/>
 9:     <property name="userName" column="user_name" not-null="true"/>
10:     <property name="userPassword" column="userpassword" not-null="true"/>
11:   </class>
12: </hibernate-mapping>

実行結果は

 1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 2:       as userpass3_4_ from users users0_ where users0_.user_id=8
 3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_2_, usersinfo0_.age as age5_2_, usersinfo0_.sex
 4:       as sex5_2_, usersinfo0_.favorite as favorite5_2_, users1_.user_id as user1_4_0_, users1_.user_name
 5:        as user2_4_0_, users1_.userpassword as userpass3_4_0_, usersinfo2_.user_info_id as user1_5_1_, usersinfo2_.age
 6:         as age5_1_, usersinfo2_.sex as sex5_1_, usersinfo2_.favorite as favorite5_1_ from user_info usersinfo0_
 7:          left outer join users users1_ on usersinfo0_.user_info_id=users1_.user_id
 8:           left outer join user_info usersinfo2_ on users1_.user_id=usersinfo2_.user_info_id
 9:            where usersinfo0_.user_info_id=?
10:      [java] ============================================
11:      [java] user_id[8] user_name[aaa] userPassword[aaa] 
12:      [java] Hibernate: delete from user_info where user_info_id=?
13:      [java] Hibernate: delete from users where user_id=?
14:      [java] ----------------------------------------
15:      [java] delete complete:aaa

UserInfo.hbmのconstrained="true"に戻して同じ事を行うと、結果は

 1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 2:       as userpass3_4_ from users users0_ where users0_.user_id=1
 3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_0_, usersinfo0_.age as age5_0_, usersinfo0_.sex
 4:       as sex5_0_, usersinfo0_.favorite as favorite5_0_ from user_info usersinfo0_ where usersinfo0_.user_info_id=?
 5:      [java] ============================================
 6:      [java] user_id[1] user_name[hoge] userPassword[fuga] 
 7:      [java] Hibernate: delete from user_info where user_info_id=?
 8:      [java] Hibernate: delete from users where user_id=?
 9:      [java] ----------------------------------------
10:      [java] delete complete:hoge

というようにUserInfo.hbmがconstrained="false"でも"true"でも削除が行えてしまった。

つまり、

cascade=\"true\"の側からしかdeleteを行えない

ということが分かる。

ではconstrainedの設定はどうかかわってくるのか?

Users.hbm.xml側にconstrained=\"true\"を設定してみる

結果は

 1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 2:       as userpass3_4_ from users users0_ where users0_.user_id=9
 3:      [java] ============================================
 4:      [java] user_id[9] user_name[iii] userPassword[iii] 
 5:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_0_, usersinfo0_.age as age5_0_, usersinfo0_.sex
 6:       as sex5_0_, usersinfo0_.favorite as favorite5_0_ from user_info usersinfo0_ where usersinfo0_.user_info_id=?
 7:      [java] Hibernate: delete from users where user_id=?
 8:      [java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
 9:      [java] 	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
10:      [java] 	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
11:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
12:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:114)
13:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:109)
14:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:244)
15:      [java] 	at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2507)
16:      [java] 	at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2725)
17:      [java] 	at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:97)
18:      [java] 	at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
19:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
20:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
21:      [java] 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
22:      [java] 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
23:      [java] 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
24:      [java] 	at com.chikkun.hibernate.sato.database.dao.UsersDAO.delete(UsersDAO.java:121)
25:      [java] 	at com.chikkun.hibernate.sato.main.Main3.main(Main3.java:34)
26:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
27:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
28:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
29:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
30:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
31:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
32:      [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:747)
33:      [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:201)
34:      [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:104)
35:      [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
36:      [java] 	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
37:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
38:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
39:      [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
40:      [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
41:      [java] 	at org.apache.tools.ant.Target.execute(Target.java:357)
42:      [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:385)
43:      [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
44:      [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
45:      [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
46:      [java] 	at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
47:      [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
48:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
49:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)
50:      [java] Caused by: java.sql.BatchUpdateException: Batch entry 0 delete from users where user_id=9 was aborted.  Call getNextException to see the cause.
51:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
52:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
53:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
54:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
55:      [java] 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
56:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
57:      [java] 	... 38 more

またConstraintViolationExceptionがおきてしまう。

UsersInfo.hbmにのみcascade=\"all\",constrained=\"true\"を設定し、UsersInfo側から削除を行うと、両方のテーブルのデータの削除が行えるが

Users.hbm側のみにcascade=\"all\",constrained=\"true\"を設定してUsers側から削除を行うと、上記のExceptionが発生する。

constrained(制約)についてネットで検索してみると

foreign識別子生成戦略を使う側に、one-to-oneのconstrainedプロパティに"true"が指定される必要がある。

とあったので

このプロジェクトだとUsersInfoのhbmのone-to-oneタグにconstrained=\"true\"が必要になる。

ためしにUsers,UsersInfo両方のhbmのone-to-oneタグからconstrained="true"の記述を削除して、

UsersInfo側のcascade=\"true\"の場合に、UsersInfo側から削除を行ってみる。

 1:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_, usersinfo0_.age as age5_, usersinfo0_.sex
 2:       as sex5_, usersinfo0_.favorite as favorite5_ from user_info usersinfo0_ where usersinfo0_.user_info_id=11
 3:      [java] Hibernate: select users0_.user_id as user1_4_2_, users0_.user_name as user2_4_2_, users0_.userpassword
 4:       as userpass3_4_2_, usersinfo1_.user_info_id as user1_5_0_, usersinfo1_.age as age5_0_, usersinfo1_.sex
 5:        as sex5_0_, usersinfo1_.favorite as favorite5_0_, users2_.user_id as user1_4_1_, users2_.user_name
 6:        as user2_4_1_, users2_.userpassword as userpass3_4_1_ from users users0_
 7:         left outer join user_info usersinfo1_ on users0_.user_id=usersinfo1_.user_info_id
 8:          left outer join users users2_ on usersinfo1_.user_info_id=users2_.user_id where users0_.user_id=?
 9:      [java] ============================================
10:      [java] userInfoId[11] age[30] sex[2] favorite[table tennis] 
11:      [java] Hibernate: delete from users where user_id=?
12:      [java] org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
13:      [java] 	at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:94)
14:      [java] 	at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
15:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:275)
16:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:114)
17:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareStatement(AbstractBatcher.java:109)
18:      [java] 	at org.hibernate.jdbc.AbstractBatcher.prepareBatchStatement(AbstractBatcher.java:244)
19:      [java] 	at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2507)
20:      [java] 	at org.hibernate.persister.entity.AbstractEntityPersister.delete(AbstractEntityPersister.java:2725)
21:      [java] 	at org.hibernate.action.EntityDeleteAction.execute(EntityDeleteAction.java:97)
22:      [java] 	at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
23:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
24:      [java] 	at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:172)
25:      [java] 	at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:321)
26:      [java] 	at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:50)
27:      [java] 	at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1027)
28:      [java] 	at com.chikkun.hibernate.sato.database.dao.UsersInfoDAO.delete(UsersInfoDAO.java:119)
29:      [java] 	at com.chikkun.hibernate.sato.main.Main3.main(Main3.java:59)
30:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
31:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
32:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
33:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
34:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
35:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
36:      [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:747)
37:      [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:201)
38:      [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:104)
39:      [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
40:      [java] 	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
41:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
42:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
43:      [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
44:      [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
45:      [java] 	at org.apache.tools.ant.Target.execute(Target.java:357)
46:      [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:385)
47:      [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
48:      [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
49:      [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
50:      [java] 	at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
51:      [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
52:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
53:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)
54:      [java] Caused by: java.sql.BatchUpdateException: Batch entry 0 delete from users where user_id=11 was aborted.
55:        Call getNextException to see the cause.
56:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement$BatchResultHandler.handleError(AbstractJdbc2Statement.java:2537)
57:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:1328)
58:      [java] 	at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:351)
59:      [java] 	at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:2674)
60:      [java] 	at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:70)
61:      [java] 	at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:268)
62:      [java] 	... 38 more

UsersInfoのhbmのone-to-oneタグの方を、constrained="true"とするのが必須のようだ。

Update

今度はupdateを検証してみる。検証結果が分かりやすいように、Users.hbm.xmlとUsersInfo.hbm.xmlのcascadeとconstrainedを初期値に戻す。

ファイル名 cascade constrained
Users.hbm.xml none false
UsersInfo.hbm.xml none false

検証その1

Users.getUsersInfo()で取得したUsersInfoインスタンスに、年齢、趣味、性別をセットしてみる。

Usersをupdate

 1:         SessionManager.initialize();
 2:         Integer id = 11;
 3:         
 4:         UsersDAO udao = new UsersDAO();
 5: 
 6:         
 7:         try {
 8:             Users users = udao.findByUserId(id);
 9:             
10:             if(users != null && users instanceof Users) {
11:                 
12:                 users.getUsersInfo().setAge(10);
13:                 users.getUsersInfo().setFavorite("hoge");
14:                 users.getUsersInfo().setSex(1);
15:                 
16:                 udao.update(users);
17:             }
18:         } catch (Exception e) {
19:             e.printStackTrace();
20:         }

実行結果は

1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
2:       as userpass3_4_ from users users0_ where users0_.user_id=11
3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_2_, usersinfo0_.age as age5_2_, usersinfo0_.sex
4:       as sex5_2_, usersinfo0_.favorite as favorite5_2_, users1_.user_id as user1_4_0_, users1_.user_name as user2_4_0_, users1_.userpassword as userpass3_4_0_, usersinfo2_.user_info_id as user1_5_1_, usersinfo2_.age as age5_1_, usersinfo2_.sex
5:        as sex5_1_, usersinfo2_.favorite as favorite5_1_ from user_info usersinfo0_ left outer join users users1_ 
6:        on usersinfo0_.user_info_id=users1_.user_id left outer join user_info usersinfo2_ 
7:        on users1_.user_id=usersinfo2_.user_info_id where usersinfo0_.user_info_id=?
8:      [java] Hibernate: update users set user_name=?, userpassword=? where user_id=?

updateは何故か、

[java] Hibernate: update users set user_name=?, userpassword=? where user_id=?

users.user_name, users.userpassword, users.user_idに対して行われている。

[<figure src="" alt=""></figure> is illegal in <p>]

検証その2

次は、

ファイル名 cascade constrained
Users.hbm.xml none true
UsersInfo.hbm.xml none false

この状態で再び同じupdateを実行する。

実行結果は

 1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
 2:       as userpass3_4_ from users users0_ where users0_.user_id=11
 3:      [java] org.hibernate.LazyInitializationException: could not initialize proxy - no Session
 4:      [java] 	at org.hibernate.proxy.AbstractLazyInitializer.initialize(AbstractLazyInitializer.java:86)
 5:      [java] 	at org.hibernate.proxy.AbstractLazyInitializer.getImplementation(AbstractLazyInitializer.java:140)
 6:      [java] 	at org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer.invoke(JavassistLazyInitializer.java:190)
 7:      [java] 	at com.chikkun.hibernate.sato.UsersInfo_$$_javassist_0.setAge(UsersInfo_$$_javassist_0.java)
 8:      [java] 	at com.chikkun.hibernate.sato.main.Main4.main(Main4.java:23)
 9:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
10:      [java] 	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
11:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
12:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
13:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.run(ExecuteJava.java:217)
14:      [java] 	at org.apache.tools.ant.taskdefs.ExecuteJava.execute(ExecuteJava.java:152)
15:      [java] 	at org.apache.tools.ant.taskdefs.Java.run(Java.java:747)
16:      [java] 	at org.apache.tools.ant.taskdefs.Java.executeJava(Java.java:201)
17:      [java] 	at org.apache.tools.ant.taskdefs.Java.execute(Java.java:104)
18:      [java] 	at org.apache.tools.ant.UnknownElement.execute(UnknownElement.java:288)
19:      [java] 	at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
20:      [java] 	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
21:      [java] 	at java.lang.reflect.Method.invoke(Method.java:597)
22:      [java] 	at org.apache.tools.ant.dispatch.DispatchUtils.execute(DispatchUtils.java:105)
23:      [java] 	at org.apache.tools.ant.Task.perform(Task.java:348)
24:      [java] 	at org.apache.tools.ant.Target.execute(Target.java:357)
25:      [java] 	at org.apache.tools.ant.Target.performTasks(Target.java:385)
26:      [java] 	at org.apache.tools.ant.Project.executeSortedTargets(Project.java:1329)
27:      [java] 	at org.apache.tools.ant.Project.executeTarget(Project.java:1298)
28:      [java] 	at org.apache.tools.ant.helper.DefaultExecutor.executeTargets(DefaultExecutor.java:41)
29:      [java] 	at org.eclipse.ant.internal.ui.antsupport.EclipseDefaultExecutor.executeTargets(EclipseDefaultExecutor.java:32)
30:      [java] 	at org.apache.tools.ant.Project.executeTargets(Project.java:1181)
31:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.run(InternalAntRunner.java:423)
32:      [java] 	at org.eclipse.ant.internal.ui.antsupport.InternalAntRunner.main(InternalAntRunner.java:137)

LazyInitializationExceptionが発生し、これもアップデートできず。

検証その3

今度は、

ファイル名 cascade constrained
Users.hbm.xml none false
UsersInfo.hbm.xml none true
1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
2:       as userpass3_4_ from users users0_ where users0_.user_id=11
3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_0_, usersinfo0_.age as age5_0_, usersinfo0_.sex
4:       as sex5_0_, usersinfo0_.favorite as favorite5_0_ from user_info usersinfo0_ where usersinfo0_.user_info_id=?
5:      [java] Hibernate: update users set user_name=?, userpassword=? where user_id=?

これも、updateは何故か、

[java] Hibernate: update users set user_name=?, userpassword=? where user_id=?

users.user_name, users.userpassword, users.user_idに対して行われている。

updateは失敗。

検証その4

では、これならどうだろうか。

ファイル名 cascade constrained
Users.hbm.xml all false
UsersInfo.hbm.xml none true
1:      [java] Hibernate: select users0_.user_id as user1_4_, users0_.user_name as user2_4_, users0_.userpassword
2:       as userpass3_4_ from users users0_ where users0_.user_id=11
3:      [java] Hibernate: select usersinfo0_.user_info_id as user1_5_0_, usersinfo0_.age as age5_0_, usersinfo0_.sex
4:       as sex5_0_, usersinfo0_.favorite as favorite5_0_ from user_info usersinfo0_ where usersinfo0_.user_info_id=?
5:      [java] Hibernate: update users set user_name=?, userpassword=? where user_id=?
6:      [java] Hibernate: update user_info set age=?, sex=?, favorite=? where user_info_id=?

これでようやく望みどおりの

[java] Hibernate: update user_info set age=?, sex=?, favorite=? \
    where user_info_id=?

が実行され、

[<figure src="" alt=""></figure> is illegal in <p>]

user_infoテーブルにアップデートを行うことができた。

INVERSEについてのメモ

[<p></p> is illegal in <strong>][<p></p> is illegal in <strong>]

one-to-oneの「N+1問題」

  • one-to-oneの「N+1問題」は不適切なone-to-one(1対1)マッピングを行う際、よく起きてしまう。

    データを取得するのに、データの件数分(N)+ 1回のSQL文をデータベースに対して発行してしまう。

    (全レコードの取得に一回+各レコード分だけ SQL を発行)

    その結果、データベースに過大な負荷をかけてしまい、パフォーマンス低下を招く。

参考にしたサイト

例として、夫・妻関係を1対1とし、主たるテーブルとして夫(HUSBAND)、従たるテーブルとして(WIFE)を設定する。

テーブルの定義

husband TABLE  

 CREATE TABLE HUSBAND (  
 ID CHAR(32) NOT NULL,  
 NAME CHAR(32) NOT NULL,  
 PRIMARY KEY(id)  
 )  

 wife TABLE  

 CREATE TABLE WIFE (  
 ID CHAR(32) NOT NULL,  
 NAME CHAR(32) NOT NULL,  
 HUSBAND_ID CHAR(32) NOT NULL,  
 PRIMARY KEY(id),  
 FOREIGN KEY(HUSBAND_ID) REFERENCE HUSBAND(ID)  
 )  

HUSBAND_IDが外部キー。HUSBANDと関連付ける。

Husband.hbm.xmlに次のようなone-to-oneマッピングがあるとする。

Husband.hbm.xml

     <one-to-one  
           name="wife"  
           cascade="all"  
           class="mypackage.Wife"  
           property-ref="husbandId"  >  
       </one-to-one>

次のようなHQL(またはCriteria)で

    FROM Husband h WHERE h.name = :name

あるいは、他のオブジェクトとJOINでクエリを実行し、

    FROM Husband h, Family f WHERE h.name = :name AND f.hid = h.id

条件に合致するデータがN件存在した場合、N+1件のSQL文が発行される。

 SELECT * FROM HUSBAND h WHERE h.name=?  
 SELECT * FROM WIFE w WHERE w.HUSBAND_ID=?  
 SELECT * FROM WIFE w WHERE w.HUSBAND_ID=?  
 SELECT * FROM WIFE w WHERE w.HUSBAND_ID=?  
 ... 

原因は、HUSBANDテーブルの中にWIFEのキーを持っていないから。

Hibernateは、Husbandというオブジェクトを取得した後、次のようなSQLで

関連するWIFEオブジェクトを取りにいき、N+1問題が発生する。

SELECT * FROM WIFE WHERE HUSBAND_ID=?

N+1問題の回避策

テーブルの再定義

外部キーを主たるテーブルに持たせる。

    #  
    # husband TABLE  
    #  
    CREATE TABLE HUSBAND (  
    ID CHAR(32) NOT NULL,  
    NAME CHAR(32) NOT NULL,  
    WIFE_ID CHAR(32) NOT NULL,  
    ^^^^^^^^^^^^^^^^^^^^^^^^^^
    PRIMARY KEY(id),  
    FOREIGN KEY(WIFE_ID) REFERENCE WIFE(ID)  
   ) 
 

次に、主従テーブルのマッピングをone-to-oneからmany-to-oneに変更する。

(one-to-oneマッピングを避ける方法。他の方法もある。)

Husband.hbm.xml

     <many-to-one  
           name="wife" column="WIFE_ID"
           not-null="false"
           cascade="all"
           class="mypackage.Wife">


       </many-to-one>

追記:リレーショナルモデル

リレーショナルデータベース構造(Structure of Relational Database

主キー(primary key)

テーブルの中の1つの行を特定するために使う列のこと。

候補キーの中からデータベースの作成者がそのテーブルのキーとして採用したキー。

候補キー(candidate key)

テーブルによっては、主キーとなりうる列が複数あることがある。

主キーとなりうる列のことを候補キーと呼ぶ。

候補キーの中で、主キーとして選ばれなかったキーを代替キー(surrogate key)と呼ぶ。

連結キー

複数の列の組み合わせによって構成されるキーのこと。

複合キーとも呼ぶ。

外部キー(foreign key)

外部キーとは、他のテーブルの参照に使用するキーのこと。

参照するテーブルの主キーが外部キーとして用いられる。