Сессия Hibernate – это интерфейс между приложением на Java и фреймворком Hibernate. Сегодня мы рассмотрим важные методы сессии для сохранения и обновления данных в таблицах – save, saveOrUpdate, persist, update и merge.
Сессия Hibernate
Сохранение сессии Hibernate
Как следует из названия метода, hibernate save() может быть использован для сохранения сущности в базе данных. Мы можем вызывать этот метод вне транзакции, вот почему я не люблю использовать этот метод для сохранения данных. Если мы используем его без транзакции и имеем каскадные связи между сущностями, то только основная сущность будет сохранена если мы не сбросим сессию. Для тестирования у нас есть два бина сущностей – Employee
и Address
.
package com.journaldev.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;
//Методы доступа к данным
@Override
public String toString() {
return "Id= " + id + ", Name= " + name + ", Salary= " + salary
+ ", {Address= " + address + "}";
}
}
package com.journaldev.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;
//Методы доступа к данным
@Override
public String toString() {
return "AddressLine1= " + addressLine1 + ", City=" + city
+ ", Zipcode=" + zipcode;
}
}
Вот простая программа Hibernate, где мы вызываем метод save()
в различных случаях.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Address;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateSaveExample {
public static void main(String[] args) {
// Подготовительные работы
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//пример сохранения - без транзакции
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("*****");
//пример сохранения - с транзакцией
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("*****");
//пример сохранения - существующая строка в таблице
Session session6 = sessionFactory.openSession();
Transaction tx6 = session6.beginTransaction();
Employee emp6 = (Employee) session6.load(Employee.class, new Long(20));
//обновление данных
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("*****");
// Закрытие ресурсов
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
*****
Несколько важных моментов, которые мы можем подтвердить из выходных данных:
- Мы должны избегать сохранения вне границы транзакции, в противном случае отображаемые сущности не будут сохранены, что вызовет несогласованность данных. Легко забыть сброс сессии, потому что он не вызывает исключений или предупреждений.
- Метод сохранения Hibernate возвращает сгенерированный идентификатор сразу же, это возможно, потому что основной объект сохраняется сразу после вызова метода сохранения.
- Если есть другие объекты, сопоставленные с основным объектом, они сохраняются при подтверждении транзакции или при сбросе сессии.
- Для объектов, находящихся в состоянии постоянства, метод сохранения обновляет данные с помощью запроса на обновление. Обратите внимание, что это происходит при подтверждении транзакции. Если в объекте нет изменений, запросы на обновление не будут выполнены. Если вы запустите вышеуказанную программу несколько раз, вы заметите, что запросы на обновление не будут выполнены в следующий раз, потому что изменений в значениях столбцов нет.
- Hibernate сохраняет объект сущности в постоянный контекст, если вы обновите свойства объекта после вызова метода сохранения, но перед подтверждением транзакции, они будут сохранены в базе данных.
Hibernate Persist
Hibernate persist похож на save (с транзакцией) и добавляет объект сущности в постоянный контекст, так что любые последующие изменения отслеживаются. Если свойства объекта изменены перед подтверждением транзакции или сеанса очистки, они также будут сохранены в базе данных. Второе отличие заключается в том, что мы можем использовать метод persist()
только в пределах транзакции, поэтому это безопасно и заботится о любых каскадных объектах. Наконец, persist ничего не возвращает, поэтому нам нужно использовать сохраненный объект для получения сгенерированного идентификатора. Давайте рассмотрим пример использования persist в Hibernate с простой программой.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernatePersistExample {
public static void main(String[] args) {
// Подготовительные работы
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
// Пример использования persist - с транзакцией
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("*****");
// Закрытие ресурсов
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=?
*****
Обратите внимание, что первый объект сотрудника вставляется, затем при подтверждении транзакции выполняется запрос на обновление значения имени. Также адрес сопоставленного объекта сохраняется в базе данных.
Hibernate saveOrUpdate
Сохранение или обновление хибернейтом приводит к выполнению запросов на вставку или обновление на основе предоставленных данных. Если данные уже присутствуют в базе данных, выполняется запрос на обновление. Мы можем использовать saveOrUpdate()
также без транзакции, но снова вы столкнетесь с проблемами, если объекты не отображены и сессия не сброшена. Hibernate saveOrUpdate добавляет объект сущности в постоянный контекст и отслеживает любые дальнейшие изменения. Любые последующие изменения сохраняются во время подтверждения транзакции, как и при использовании persist.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateSaveOrUpdateExample {
public static void main(String[] args) {
// Подготовка
SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
//Пример saveOrUpdate - без транзакции
Session session5 = sessionFactory.openSession();
Employee emp5 = HibernateSaveExample.getTestEmployee();
session5.saveOrUpdate(emp5);
System.out.println("*****");
//Пример saveOrUpdate - с транзакцией
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("*****");
// Закрытие ресурсов
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
*****
Обратите внимание, что без транзакции сохраняется только объект Employee, и информация об адресе теряется. С транзакцией объект сотрудника отслеживается на предмет каких-либо изменений, поэтому в последнем вызове не происходит обновление в таблице Employee, даже если значение было изменено между вызовами, окончательное значение остается неизменным.
Обновление хибернейтом
Обновление хибернейтом следует использовать там, где мы знаем, что обновляем только информацию о сущности. Эта операция добавляет объект сущности в постоянный контекст, и дальнейшие изменения отслеживаются и сохраняются при подтверждении транзакции. Давайте проверим это поведение с помощью простой программы.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateUpdateExample {
public static void main(String[] args) {
// Подготовка
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();
// обновление примера
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");
// Закрытие ресурсов
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 отслеживал объект на предмет изменений, и во время фиксации транзакции это значение было сохранено.
Слияние Hibernate
Слияние Hibernate может использоваться для обновления существующих значений, однако этот метод создает копию из переданного объекта сущности и возвращает ее. Возвращенный объект является частью постоянного контекста и отслеживается на наличие изменений, переданный объект не отслеживается. Это основное отличие слияния () от всех остальных методов. Давайте рассмотрим это на простом примере.
package com.journaldev.hibernate.main;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import com.journaldev.hibernate.model.Employee;
import com.journaldev.hibernate.util.HibernateUtil;
public class HibernateMergeExample {
public static void main(String[] args) {
// Подготовка
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();
// пример слияния - данные уже присутствуют в таблицах
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");
// Закрытие ресурсов
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
Обратите внимание, что объект entity, возвращаемый merge(), отличается от переданного объекта entity. Также обратите внимание, что в дальнейшем выполнении name равен “Кумар”, это потому что возвращаемый объект отслеживается на предмет любых изменений. Это все, что касается методов Сеанс Hibernate save
и update
. Надеюсь, приведенные выше примеры помогут вам прояснить все сомнения.