웹사이트 검색

Hibernate 세션 병합, 업데이트, 저장, saveOrUpdate, 지속 예제


Hibernate Session은 Java 애플리케이션과 Hibernate 프레임워크 사이의 인터페이스입니다. 오늘 우리는 테이블에서 데이터를 저장하고 업데이트하기 위한 세션의 중요한 방법(save, saveOrUpdate, persist, update 및 merge)을 살펴볼 것입니다.

최대 절전 모드 세션

Hibernate 세션 저장

메서드 이름에서 알 수 있듯이 최대 절전 모드 save()를 사용하여 엔터티를 데이터베이스에 저장할 수 있습니다. 트랜잭션 외부에서 이 메서드를 호출할 수 있으므로 데이터를 저장하는 이 메서드가 마음에 들지 않습니다. 트랜잭션 없이 이것을 사용하고 엔터티 간에 캐스케이딩이 있는 경우 세션을 플러시하지 않는 한 기본 엔터티만 저장됩니다. 테스트 목적으로 EmployeeAddress라는 두 개의 엔티티 bean이 있습니다.

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;

        //Getter setter methods

	@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;

        //Getter setter methods

	@Override
	public String toString() {
		return "AddressLine1= " + addressLine1 + ", City=" + city
				+ ", Zipcode=" + zipcode;
	}
}

다음은 다양한 경우에 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) {
		
		// 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
*****

위 출력에서 확인할 수 있는 몇 가지 중요한 사항은 다음과 같습니다.

  • 트랜잭션 경계 외부에 저장하지 않아야 합니다. 그렇지 않으면 매핑된 엔터티가 저장되지 않아 데이터 불일치가 발생합니다. 예외나 경고가 발생하지 않기 때문에 세션 플러시를 잊어버리는 것은 매우 정상적인 일입니다.
  • Hibernate 저장 방법은 생성된 ID를 즉시 반환합니다. 이것은 저장 방법이 호출되는 즉시 기본 객체가 저장되기 때문에 가능합니다.
  • 기본 개체에서 매핑된 다른 개체가 있는 경우 트랜잭션을 커밋하거나 세션을 플러시할 때 저장됩니다.
  • 지속 상태에 있는 개체의 경우 업데이트 쿼리를 통해 데이터를 업데이트합니다. 트랜잭션이 커밋될 때 발생합니다. 개체에 변경 사항이 없으면 쿼리가 실행되지 않습니다. 위의 프로그램을 여러 번 실행하면 열 값에 변화가 없기 때문에 다음에 업데이트 쿼리가 실행되지 않는다는 것을 알 수 있습니다.
  • 저장 호출 후 그러나 트랜잭션이 커밋되기 전에 개체 속성을 업데이트하면 데이터베이스에 저장됩니다.

최대 절전 모드 지속

Hibernate 지속은 저장(트랜잭션 포함)과 유사하며 엔터티 개체를 영구 컨텍스트에 추가하므로 추가 변경 사항이 추적됩니다. 트랜잭션이 커밋되거나 세션이 플러시되기 전에 개체 속성이 변경되면 데이터베이스에도 저장됩니다. 두 번째 차이점은 트랜잭션의 경계 내에서만 persist() 메서드를 사용할 수 있으므로 안전하고 계단식 개체를 처리한다는 것입니다. 마지막으로, 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 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=?
*****

첫 번째 직원 개체가 삽입된 다음 트랜잭션 커밋 시 업데이트 쿼리가 실행되어 이름 값을 업데이트합니다. 또한 매핑된 객체 주소는 데이터베이스에 저장됩니다.

최대 절전 모드 저장 또는 업데이트

Hibernate saveOrUpdate 결과를 제공된 데이터를 기반으로 쿼리를 삽입하거나 업데이트합니다. 데이터가 데이터베이스에 있으면 업데이트 쿼리가 실행됩니다. 트랜잭션 없이 saveOrUpdate()를 사용할 수도 있지만 세션이 플러시되지 않으면 매핑된 개체가 저장되지 않는 문제에 다시 직면하게 됩니다. Hibernate saveOrUpdate는 엔터티 개체를 영구 컨텍스트에 추가하고 추가 변경 사항을 추적합니다. 추가 변경 사항은 지속과 같이 트랜잭션을 커밋할 때 저장됩니다.

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) {
		
		// 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
*****

트랜잭션이 없으면 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) {

		// 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가 모든 변경 사항에 대해 객체를 추적하고 있었고 트랜잭션을 커밋할 때 이 값이 저장되었음을 확인합니다.

최대 절전 모드 병합

Hibernate 병합은 기존 값을 업데이트하는 데 사용할 수 있지만 이 방법은 전달된 엔터티 개체에서 복사본을 만들고 반환합니다. 반환된 개체는 영구 컨텍스트의 일부이며 모든 변경 사항을 추적합니다. 전달된 개체는 추적되지 않습니다. 이것이 다른 모든 메서드와 merge()의 주요 차이점입니다. 간단한 프로그램으로 살펴보자.

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) {

		// 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 세션 saveupdate 메서드에 대한 모든 것입니다. 위의 예가 귀하의 의심을 명확히 하는 데 도움이 되기를 바랍니다.