hibernate一対多(one-to-many)双方向関連
一対多(one-to-many)双方向関連インスタンス(Department-Employee)
ここでの一対の多双方向関連付けは、ドメインモデル(実体オブジェクトモデル)上の概念である、リレーショナルデータベースでは、外部キー参照関係のみが存在し、常に「many」側が「one」側を参照するので、冗長データを除去することができる、上リレーショナルデータベースは実際には多対一または一対一の一方向関連付けのみをサポートするためである.エンティティ(クラスとクラスの間)の様々な関係の中で、数の多対一の一方向関連関係はデータベース中の外部キー参照関係と最も一致している.
私の前の文章の多対一に基づいて修正します.
1.Departmentエンティティクラスを変更するには、次の手順に従います.
2.Departmentを変更します.hbm.xmlファイル:
要素の属性name:マッピング対象の永続化クラスの属性名を設定、ここではDepatmentクラスのemps属性とする.
要素の2つのサブ要素:
:関連する永続化クラスに対応するテーブルの外部キーを設定します.ここではEmployeeテーブルのdepart_idフィールド
:関連する永続化クラスを設定します.ここではEmployee.
Hibernateは、上記の構成情報に基づいて、次の情報を取得します.
要素はDepartmentクラスのemps属性がjavaであることを示す.util.セットタイプ
サブエレメントは、empsコレクションに格納されているEmployeeオブジェクトのセットを示します.
サブエレメントは、employeeテーブルが外部キーdepart_を介していることを示します.idはdepartmentテーブルを参照する.
3.残りはそのままで、テストクラスは次のとおりです.
コンソールの印刷情報は次のとおりです.
Hibernate: insert into Department (name) values (?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=? Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=?
emp size:2
次のようにデータベースに記録されます.
mysql> select * from employee; +----+-----------------+-----------+ | id | name | depart_id | +----+-----------------+-----------+ | 1 | employee1 name2 | 1 | | 2 | employee2 name2 | 1 | +----+-----------------+-----------+ 2 rows in set (0.00 sec) mysql> select * from department; +----+-----------------+ | id | name | +----+-----------------+ | 1 | department name | +----+-----------------+ 1 row in set (0.00 sec)
オブジェクトモデルは変更されましたが、データベース関係モデルは変更されていません.
テストプログラムの注釈部分を削除し、同時に「オブジェクトモデルを作成する」という注釈を削除すると、正しく実行できますが、コンソールには2つの更新文が印刷され、データベースが2回の更新操作を実行したことを示し、効率に影響があります.以下に示します.
Hibernate: insert into Department (name) values (?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: insert into Employee (name, depart_id) values (?, ?)Hibernate: update Employee set depart_id=? where id=? Hibernate: update Employee set depart_id=? where id=? Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=? Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=? emp size:2
4.怠惰ロード分析:
(1)プログラム中に1と表記されているプログラム注釈を削除した場合、コンソールは以下のように情報を印刷する.
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=?
この場合はDepartmentオブジェクトのみがクエリされ、対応するEmployeeはクエリされません.
(2)プログラムの中で1と表記されているプログラムの注釈を削除し、同時に2と表記されている前の注釈を削除した場合、プログラムを実行し、コンソール印刷情報は以下の通りである.
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=? emps class:class org.hibernate.collection.PersistentSet
このとき、Hibernateがhashsetを自分で書いたPersistentSetに置き換えていることがわかり、怠惰なロード機能を実現することができます.同時にdepartmentが呼び出される.getEmps();hibernateは、集合エージェントオブジェクトの参照を取得しただけで、集合オブジェクトを埋め込むためにデータベースをクエリーしません.この問題を検証するためにさらにテストすることもできます.プログラムに1とマークされているプログラム注釈を削除し、3とマークされている文の前の注釈を削除すると、プログラムが正常に動作していることがわかりますが、4とマークされている文の前の注釈を削除してプログラムを実行すると、次のような異常が放出されます.
org.hibernate.LazyInitializationException : failed to lazily initialize a collection of role: com.reiyen.hibernate.domain.Department.emps, no session or session was closed
(3)departmentオブジェクトクエリを行う場合,departmentのテーブル構造からもこのDepartmentオブジェクトに対応するEmployeeがあるかどうかは判断できないが,なぜ一対一クエリではない場合にも,関連するオブジェクトを一度にクエリするのではないか.これは、関連するオブジェクトをクエリーすると、データが多いため効率に深刻な影響を及ぼすため、一対一ではなく、いずれにしてもオブジェクトから1つしかなく、一度にクエリーするとあまり影響がないため、hibernateは対応するEmployeeがあると仮定し、集合エージェントオブジェクトの戻りを直接作成した(オブジェクトを参照して先に渡す)ためです.データが必要なときにデータベースをクエリーして、効率を高めます.
ここでの一対の多双方向関連付けは、ドメインモデル(実体オブジェクトモデル)上の概念である、リレーショナルデータベースでは、外部キー参照関係のみが存在し、常に「many」側が「one」側を参照するので、冗長データを除去することができる、上リレーショナルデータベースは実際には多対一または一対一の一方向関連付けのみをサポートするためである.エンティティ(クラスとクラスの間)の様々な関係の中で、数の多対一の一方向関連関係はデータベース中の外部キー参照関係と最も一致している.
私の前の文章の多対一に基づいて修正します.
1.Departmentエンティティクラスを変更するには、次の手順に従います.
package com.reiyen.hibernate.domain
public class Department {
private int id;
private String name;
private Set<Employee> emps;
//setter getter
}
2.Departmentを変更します.hbm.xmlファイル:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.reiyen.hibernate.domain">
<class name="Department" >
<id name="id" >
<generator class="native" />
</id>
<property name="name" />
<set name="emps">
<key column="depart_id" />
<one-to-many class="Employee"/>
</set>
</class>
</hibernate-mapping>
Hibernateは、上記の構成情報に基づいて、次の情報を取得します.
3.残りはそのままで、テストクラスは次のとおりです.
public class Many2One {
/**
* @param args
*/
public static void main(String[] args) {
Department depart = add();
Department department = queryDepart(depart.getId());
//department.getEmps();3
// System.out.println("emp size:" + department.getEmps().size());4
}
static Department queryDepart(int departId) {
Session s = null;
Transaction tx = null;
try {
s = HibernateUtil.getSession();
tx = s.beginTransaction();
Department depart = (Department) s.get(Department.class, departId);
System.out.println("emp size:" + depart.getEmps().size());//1
// System.out.println("emps class:" + depart.getEmps().getClass()); //2
// Hibernate.initialize(depart.getEmps());
tx.commit();
return depart;
} finally {
if (s != null)
s.close();
}
}
static Department add() {
Session s = null;
Transaction tx = null;
try {
Department depart = new Department();
depart.setName("department name");
Employee employee1 = new Employee();
employee1.setDepartment(depart); // :
employee1.setName("employee1 name2");
Employee employee2 = new Employee();
employee2.setDepartment(depart); // :
employee2.setName("employee2 name2");
// Set<Employee> set = new HashSet<Employee>();
// set.add(employee1);
// set.add(employee2);
// depart.setEmps(set);
s = HibernateUtil.getSession();
tx = s.beginTransaction();
s.save(depart);
s.save(employee1);
s.save(employee2);
tx.commit();
return depart;
} finally {
if (s != null)
s.close();
}
}
}
コンソールの印刷情報は次のとおりです.
Hibernate: insert into Department (name) values (?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=? Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=?
emp size:2
次のようにデータベースに記録されます.
mysql> select * from employee; +----+-----------------+-----------+ | id | name | depart_id | +----+-----------------+-----------+ | 1 | employee1 name2 | 1 | | 2 | employee2 name2 | 1 | +----+-----------------+-----------+ 2 rows in set (0.00 sec) mysql> select * from department; +----+-----------------+ | id | name | +----+-----------------+ | 1 | department name | +----+-----------------+ 1 row in set (0.00 sec)
オブジェクトモデルは変更されましたが、データベース関係モデルは変更されていません.
テストプログラムの注釈部分を削除し、同時に「オブジェクトモデルを作成する」という注釈を削除すると、正しく実行できますが、コンソールには2つの更新文が印刷され、データベースが2回の更新操作を実行したことを示し、効率に影響があります.以下に示します.
Hibernate: insert into Department (name) values (?) Hibernate: insert into Employee (name, depart_id) values (?, ?) Hibernate: insert into Employee (name, depart_id) values (?, ?)Hibernate: update Employee set depart_id=? where id=? Hibernate: update Employee set depart_id=? where id=? Hibernate: select department0_.id as id1_0_, department0_.name as name1_0_ from Department department0_ where department0_.id=? Hibernate: select emps0_.depart_id as depart3_1_, emps0_.id as id1_, emps0_.id as id2_0_, emps0_.name as name2_0_, emps0_.depart_id as depart3_2_0_ from Employee emps0_ where emps0_.depart_id=? emp size:2
4.怠惰ロード分析:
(1)プログラム中に1と表記されているプログラム注釈を削除した場合、コンソールは以下のように情報を印刷する.
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=?
この場合はDepartmentオブジェクトのみがクエリされ、対応するEmployeeはクエリされません.
(2)プログラムの中で1と表記されているプログラムの注釈を削除し、同時に2と表記されている前の注釈を削除した場合、プログラムを実行し、コンソール印刷情報は以下の通りである.
Hibernate: select department0_.id as id0_0_, department0_.name as name0_0_ from Department department0_ where department0_.id=? emps class:class org.hibernate.collection.PersistentSet
このとき、Hibernateがhashsetを自分で書いたPersistentSetに置き換えていることがわかり、怠惰なロード機能を実現することができます.同時にdepartmentが呼び出される.getEmps();hibernateは、集合エージェントオブジェクトの参照を取得しただけで、集合オブジェクトを埋め込むためにデータベースをクエリーしません.この問題を検証するためにさらにテストすることもできます.プログラムに1とマークされているプログラム注釈を削除し、3とマークされている文の前の注釈を削除すると、プログラムが正常に動作していることがわかりますが、4とマークされている文の前の注釈を削除してプログラムを実行すると、次のような異常が放出されます.
org.hibernate.LazyInitializationException : failed to lazily initialize a collection of role: com.reiyen.hibernate.domain.Department.emps, no session or session was closed
(3)departmentオブジェクトクエリを行う場合,departmentのテーブル構造からもこのDepartmentオブジェクトに対応するEmployeeがあるかどうかは判断できないが,なぜ一対一クエリではない場合にも,関連するオブジェクトを一度にクエリするのではないか.これは、関連するオブジェクトをクエリーすると、データが多いため効率に深刻な影響を及ぼすため、一対一ではなく、いずれにしてもオブジェクトから1つしかなく、一度にクエリーするとあまり影響がないため、hibernateは対応するEmployeeがあると仮定し、集合エージェントオブジェクトの戻りを直接作成した(オブジェクトを参照して先に渡す)ためです.データが必要なときにデータベースをクエリーして、効率を高めます.