以下は、ネイティブな日本語での言い換え例です。Hibernateのセッションマージ、更新、保存、保存または更新、永続化の例
ネイティブな日本語で以下を言い換えると、1つのオプションで結構です:
HibernateのセッションはJavaアプリケーションとHibernateフレームワークの間のインターフェースです。今日は、テーブルにデータを保存および更新するためのセッションの重要なメソッドを見ていきます – save、saveOrUpdate、persist、update、およびmerge。
ハイバネートのセッション
Hibernateのセッション保存
方法名が示すように、hibernate save()を使用してエンティティをデータベースに保存することができます。このメソッドはトランザクションの外で呼び出すことができるため、私はこのメソッドをデータ保存には好ましくありません。トランザクションなしで使用し、エンティティ間でカスケードがある場合、セッションをフラッシュしない限り、主エンティティのみが保存されます。テスト目的のために、2つのエンティティビーン、EmployeeとAddress、があります。
package com.scdev.hibernate.model;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.Table;
import org.hibernate.annotations.Cascade;
@Entity
@Table(name = "EMPLOYEE")
@Access(value=AccessType.FIELD)
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "emp_id")
private long id;
@Column(name = "emp_name")
private String name;
@Column(name = "emp_salary")
private double salary;
@OneToOne(mappedBy = "employee")
@Cascade(value = org.hibernate.annotations.CascadeType.ALL)
private Address address;
//Getter setter methods
@Override
public String toString() {
return "Id= " + id + ", Name= " + name + ", Salary= " + salary
+ ", {Address= " + address + "}";
}
}
package com.scdev.hibernate.model;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToOne;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.Table;
import org.hibernate.annotations.GenericGenerator;
import org.hibernate.annotations.Parameter;
@Entity
@Table(name = "ADDRESS")
@Access(value=AccessType.FIELD)
public class Address {
@Id
@Column(name = "emp_id", unique = true, nullable = false)
@GeneratedValue(generator = "gen")
@GenericGenerator(name = "gen", strategy = "foreign", parameters = { @Parameter(name = "property", value = "employee") })
private long id;
@Column(name = "address_line1")
private String addressLine1;
@Column(name = "zipcode")
private String zipcode;
@Column(name = "city")
private String city;
@OneToOne
@PrimaryKeyJoinColumn
private Employee employee;
//Getter setter methods
@Override
public String toString() {
return "AddressLine1= " + addressLine1 + ", City=" + city
+ ", Zipcode=" + zipcode;
}
}
以下に、私たちが異なるケースでsave()メソッドを呼び出しているシンプルなHibernateプログラムがあります。
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.scdev.hibernate.model.Address;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernateSaveExample {
public static void main(String[] args) {
// Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//save example - without transaction
Session session = sessionFactory.openSession();
Employee emp = getTestEmployee();
long id = (Long) session.save(emp);
System.out.println("1. Employee save called without transaction, id="+id);
session.flush(); //address will not get saved without this
System.out.println("*****");
//save example - with transaction
Transaction tx1 = session.beginTransaction();
Session session1 = sessionFactory.openSession();
Employee emp1 = getTestEmployee();
long id1 = (Long) session1.save(emp1);
System.out.println("2. Employee save called with transaction, id="+id1);
System.out.println("3. Before committing save transaction");
tx1.commit();
System.out.println("4. After committing save transaction");
System.out.println("*****");
//save example - existing row in table
Session session6 = sessionFactory.openSession();
Transaction tx6 = session6.beginTransaction();
Employee emp6 = (Employee) session6.load(Employee.class, new Long(20));
//update some data
System.out.println("Employee Details="+emp6);
emp6.setName("New Name");
emp6.getAddress().setCity("New City");
long id6 = (Long) session6.save(emp6);
emp6.setName("New Name1"); // will get updated in database
System.out.println("5. Employee save called with transaction, id="+id6);
System.out.println("6. Before committing save transaction");
tx6.commit();
System.out.println("7. After committing save transaction");
System.out.println("*****");
// Close resources
sessionFactory.close();
}
public static Employee getTestEmployee() {
Employee emp = new Employee();
Address add = new Address();
emp.setName("Test Emp");
emp.setSalary(1000);
add.setAddressLine1("Test address1");
add.setCity("Test City");
add.setZipcode("12121");
emp.setAddress(add);
add.setEmployee(emp);
return emp;
}
}
上記のプログラムを実行すると、次の出力が生成されます。
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
1. Employee save called without transaction, id=149
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
*****
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
2. Employee save called with transaction, id=150
3. Before committing save transaction
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
4. After committing save transaction
*****
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee Details=Id= 20, Name= Kumar1, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Blr, Zipcode=12121}
5. Employee save called with transaction, id=20
6. Before committing save transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
7. After committing save transaction
*****
上記の出力から確認できるいくつかの重要なポイントは次のとおりです。
- We should avoid save outside transaction boundary, otherwise mapped entities will not be saved causing data inconsistency. It’s very normal to forget flushing the session because it doesn’t throw any exception or warnings.
- Hibernate save method returns the generated id immediately, this is possible because primary object is saved as soon as save method is invoked.
- If there are other objects mapped from the primary object, they gets saved at the time of committing transaction or when we flush the session.
- For objects that are in persistent state, save updates the data through update query. Notice that it happens when transaction is committed. If there are no changes in the object, there wont be any query fired. If you will run above program multiple times, you will notice that update queries are not fired next time because there is no change in the column values.
- Hibernate save load entity object to persistent context, if you will update the object properties after the save call but before the transaction is committed, it will be saved into database.
ヒバネイト Persist
Hibernateのpersistメソッドは、saveメソッドと似ています(トランザクションを含むもの)が、エンティティオブジェクトを永続的なコンテキストに追加するため、その後の変更も追跡されます。トランザクションがコミットされるかセッションがフラッシュされる前にオブジェクトのプロパティが変更された場合、それもデータベースに保存されます。2つ目の違いは、persist()メソッドはトランザクションの範囲内でしか使用できないことであり、安全で関連オブジェクトにも対応しています。最後に、persistは何も返さないため、生成された識別子値を取得するために永続化したオブジェクトを使用する必要があります。次に、シンプルなプログラムを使用してHibernateのpersistを見てみましょう。
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernatePersistExample {
public static void main(String[] args) {
// Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//persist example - with transaction
Session session2 = sessionFactory.openSession();
Transaction tx2 = session2.beginTransaction();
Employee emp2 = HibernateSaveExample.getTestEmployee();
session2.persist(emp2);
System.out.println("Persist called");
emp2.setName("Kumar"); // will be updated in database too
System.out.println("Employee Name updated");
System.out.println("8. Employee persist called with transaction, id="+emp2.getId()+", address id="+emp2.getAddress().getId());
tx2.commit();
System.out.println("*****");
// Close resources
sessionFactory.close();
}
}
上記のコードによって生成された出力は次の通りです。
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
8. Employee persist called with transaction, id=158, address id=158
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
*****
最初の従業員オブジェクトが挿入され、トランザクションのコミット時に名前の値を更新するためのクエリが実行されることに気づく。また、マップされたオブジェクトのアドレスがデータベースに保存される。
ヒベルネートのsaveOrUpdate機能
HibernateのsaveOrUpdateは、提供されたデータに基づいて、挿入または更新クエリに結果を保存します。データがデータベースに存在する場合、更新クエリが実行されます。トランザクションなしでもsaveOrUpdate()を使用することができますが、セッションがフラッシュされていない場合、マップされたオブジェクトが保存されないという問題が再び発生する可能性があります。HibernateのsaveOrUpdateはエンティティオブジェクトを永続的なコンテキストに追加し、それ以降の変更を追跡します。永続化のように、さらなる変更はトランザクションのコミット時に保存されます。
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernateSaveOrUpdateExample {
public static void main(String[] args) {
// Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//saveOrUpdate example - without transaction
Session session5 = sessionFactory.openSession();
Employee emp5 = HibernateSaveExample.getTestEmployee();
session5.saveOrUpdate(emp5);
System.out.println("*****");
//saveOrUpdate example - with transaction
Session session3 = sessionFactory.openSession();
Transaction tx3 = session3.beginTransaction();
Employee emp3 = HibernateSaveExample.getTestEmployee();
session3.saveOrUpdate(emp3);
emp3.setName("Kumar"); //will be saved into DB
System.out.println("9. Before committing saveOrUpdate transaction. Id="+emp3.getId());
tx3.commit();
System.out.println("10. After committing saveOrUpdate transaction");
System.out.println("*****");
Transaction tx4 = session3.beginTransaction();
emp3.setName("Updated Test Name"); //Name changed
emp3.getAddress().setCity("Updated City");
session3.saveOrUpdate(emp3);
emp3.setName("Kumar"); //again changed to previous value, so no Employee update
System.out.println("11. Before committing saveOrUpdate transaction. Id="+emp3.getId());
tx4.commit();
System.out.println("12. After committing saveOrUpdate transaction");
System.out.println("*****");
// Close resources
sessionFactory.close();
}
}
上記プログラムは次の出力を生成します。
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
*****
Hibernate: insert into EMPLOYEE (emp_name, emp_salary) values (?, ?)
9. Before committing saveOrUpdate transaction. Id=166
Hibernate: insert into ADDRESS (address_line1, city, zipcode, emp_id) values (?, ?, ?, ?)
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
10. After committing saveOrUpdate transaction
*****
11. Before committing saveOrUpdate transaction. Id=166
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
12. After committing saveOrUpdate transaction
*****
トランザクションがない場合、従業員のみが保存され、住所情報が失われてしまうことに注意してください。トランザクションを行うことにより、従業員オブジェクトの変更が追跡されます。そのため、最後の呼び出しでは、従業員テーブルに更新はありません。値が変更された間にも、最終的な値は同じままです。
ハイバネーションのアップデート
ネイティブの日本語で以下の文章を言い換えます。選択肢は一つだけ必要です。
エンティティ情報の更新のみを行う場合には、Hibernateのupdateメソッドを使用すべきです。この操作によって、エンティティオブジェクトが永続コンテキストに追加され、さらなる変更が追跡され、トランザクションのコミット時に保存されます。簡単なプログラムでこの挙動を確認しましょう。
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernateUpdateExample {
public static void main(String[] args) {
// Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.load(Employee.class, new Long(101));
System.out.println("Employee object loaded. " + emp);
tx.commit();
// update example
emp.setName("Updated name");
emp.getAddress().setCity("Bangalore");
Transaction tx7 = session.beginTransaction();
session.update(emp);
emp.setName("Final updated name");
System.out.println("13. Before committing update transaction");
tx7.commit();
System.out.println("14. After committing update transaction");
// Close resources
sessionFactory.close();
}
}
初めて上記のプログラムを実行すると、以下の出力が得られます。
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Test Emp, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Test City, Zipcode=12121}
13. Before committing update transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
Hibernate: update ADDRESS set address_line1=?, city=?, zipcode=? where emp_id=?
14. After committing update transaction
その後の実行により、以下の出力が得られます。
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Final updated name, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
13. Before committing update transaction
14. After committing update transaction
最初の実行後、更新は行われていないことに注目してください。なぜなら、値に更新がないからです。また、「update()」メソッドを呼び出した後に設定した従業員の名前が「最終更新名」であることにも注目してください。これにより、Hibernateがオブジェクトの変更を追跡しており、トランザクションのコミット時にこの値が保存されたことが確認されます。
ハイバネートのマージ
ハイバネートのマージ機能は既存の値を更新するために使用できますが、このメソッドは渡されたエンティティオブジェクトからコピーを作成し、それを返します。返されたオブジェクトは永続化コンテキストの一部であり、変更が追跡されますが、渡されたオブジェクトは追跡されません。これは、merge()と他のすべてのメソッドとの主な違いです。簡単なプログラムでこれを見てみましょう。
package com.scdev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.scdev.hibernate.model.Employee;
import com.scdev.hibernate.util.HibernateUtil;
public class HibernateMergeExample {
public static void main(String[] args) {
// Prep Work
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
Employee emp = (Employee) session.load(Employee.class, new Long(101));
System.out.println("Employee object loaded. " + emp);
tx.commit();
//merge example - data already present in tables
emp.setSalary(25000);
Transaction tx8 = session.beginTransaction();
Employee emp4 = (Employee) session.merge(emp);
System.out.println(emp4 == emp); // returns false
emp.setName("Test");
emp4.setName("Kumar");
System.out.println("15. Before committing merge transaction");
tx8.commit();
System.out.println("16. After committing merge transaction");
// Close resources
sessionFactory.close();
}
}
最初の実行時の出力は:
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Final updated name, Salary= 1000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
false
15. Before committing merge transaction
Hibernate: update EMPLOYEE set emp_name=?, emp_salary=? where emp_id=?
16. After committing merge transaction
さらなる実行において、出力されるものは次のとおりです。
Hibernate: select employee0_.emp_id as emp_id1_1_0_, employee0_.emp_name as emp_name2_1_0_, employee0_.emp_salary as emp_sala3_1_0_, address1_.emp_id as emp_id1_0_1_, address1_.address_line1 as address_2_0_1_, address1_.city as city3_0_1_, address1_.zipcode as zipcode4_0_1_ from EMPLOYEE employee0_ left outer join ADDRESS address1_ on employee0_.emp_id=address1_.emp_id where employee0_.emp_id=?
Employee object loaded. Id= 101, Name= Kumar, Salary= 25000.0, {Address= AddressLine1= Test address1, City=Bangalore, Zipcode=12121}
false
15. Before committing merge transaction
16. After committing merge transaction
merge()で返されるエンティティオブジェクトが、渡されたエンティティと異なることに注意してください。また、さらなる実行で名前が「Kumar」になっていることにも注意してください。これは、返されたオブジェクトが変更を追跡するためです。Hibernateのセッション保存および更新メソッドの説明は以上です。上記の例が、あなたが持っている疑問を明確にするのに役立つことを願っています。