Exemplo de mesclar, atualizar, salvar, salvar ou atualizar e persistir sessão do Hibernate

A Sessão do Hibernate é a interface entre a aplicação Java e o framework Hibernate. Hoje, vamos examinar os métodos importantes da Sessão para salvar e atualizar dados em tabelas – save, saveOrUpdate, persist, update e merge.

Sessão do Hibernate

Salvar na Sessão do Hibernate

Como o nome do método sugere, hibernate save() pode ser usado para salvar uma entidade no banco de dados. Podemos invocar este método fora de uma transação, por isso não gosto deste método para salvar dados. Se usarmos isso sem transação e tivermos cascata entre entidades, então apenas a entidade primária é salva a menos que nós “flush” a sessão. Para nossos propósitos de teste, temos duas entidades de beans – Funcionário e Endereço.

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;

        // Métodos getter e setter

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

        // Métodos getter e setter

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

Aqui está um programa Hibernate simples onde estamos invocando o método save() em diferentes casos.

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) {
		
		// Trabalho Preparatório
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		
		// exemplo de salvamento - sem transação
		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("*****");
		
		// exemplo de salvamento - com transação
		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("*****");
		
		// exemplo de salvamento - linha existente na tabela
		Session session6 = sessionFactory.openSession();
		Transaction tx6 = session6.beginTransaction();
		Employee emp6 =  (Employee) session6.load(Employee.class, new Long(20));
		
		// atualizar alguns dados
		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("*****");
		
		// Fechar recursos
		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;
	}
}

Ao executarmos o programa acima, produz o seguinte resultado.

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
*****

Alguns pontos importantes que podemos confirmar a partir do resultado acima são:

  • Devemos evitar salvar fora do limite da transação, caso contrário, as entidades mapeadas não serão salvas, causando inconsistência de dados. É muito comum esquecer de esvaziar a sessão porque não gera exceções ou avisos.
  • O método save do Hibernate retorna imediatamente o id gerado, isso é possível porque o objeto primário é salvo assim que o método save é invocado.
  • Se houver outros objetos mapeados a partir do objeto primário, eles serão salvos no momento da confirmação da transação ou quando esvaziamos a sessão.
  • Para objetos que estão em estado persistente, o save atualiza os dados por meio da consulta de atualização. Observe que isso ocorre quando a transação é confirmada. Se não houver alterações no objeto, não haverá consulta executada. Se você executar o programa acima várias vezes, notará que as consultas de atualização não são executadas na próxima vez porque não há alteração nos valores das colunas.
  • O Hibernate salva o objeto da entidade no contexto persistente, se você atualizar as propriedades do objeto após a chamada save mas antes da transação ser confirmada, elas serão salvas no banco de dados.

Hibernate Persistir

O persistir do Hibernate é semelhante ao método save (com transação) e adiciona o objeto de entidade ao contexto persistente, de modo que quaisquer alterações posteriores são rastreadas. Se as propriedades do objeto forem alteradas antes que a transação seja confirmada ou a sessão seja limpa, elas também serão salvas no banco de dados. A segunda diferença é que podemos usar o método persist() apenas dentro do limite de uma transação, tornando-o seguro e cuidando de quaisquer objetos em cascata. Finalmente, o persistir não retorna nada, então precisamos usar o objeto persistido para obter o valor do identificador gerado. Vamos dar uma olhada no persistir do Hibernate com um programa simples.

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) {
		
		// Trabalho Preparatório
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();	
		
		// Exemplo de persistir - com transação
		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("*****");
		
		// Fechar recursos
		sessionFactory.close();

	}

}

A saída produzida pelo código acima é:

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=?
*****

Observe que o primeiro objeto de funcionário é inserido e, em seguida, no momento da confirmação da transação, a consulta de atualização é executada para atualizar o valor do nome. Além disso, o endereço do objeto mapeado é salvo no banco de dados.

Salvar ou Atualizar do Hibernate

O Hibernate saveOrUpdate resulta em consultas de inserção ou atualização com base nos dados fornecidos. Se os dados estiverem presentes no banco de dados, a consulta de atualização é executada. Podemos usar saveOrUpdate() sem transação também, mas novamente você enfrentará problemas com objetos mapeados que não são salvos se a sessão não for limpa. O Hibernate saveOrUpdate adiciona o objeto da entidade ao contexto persistente e acompanha quaisquer alterações adicionais. Quaisquer alterações adicionais são salvas no momento da confirmação da transação, como persistir.

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) {
		
		// Trabalho preparatório
		SessionFactory sessionFactory = HibernateUtil.getSessionFactory();
		
		// Exemplo de saveOrUpdate - sem transação
		Session session5 = sessionFactory.openSession();
		Employee emp5 = HibernateSaveExample.getTestEmployee();
		session5.saveOrUpdate(emp5);
		System.out.println("*****");
		
		// Exemplo de saveOrUpdate - com transação
		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("*****");

		// Fechar recursos
		sessionFactory.close();

	}
}

O programa acima produz a seguinte saída.

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
*****

Observe que sem transação, apenas o funcionário é salvo e as informações de endereço são perdidas. Com a transação, o objeto do funcionário é rastreado para quaisquer alterações, por isso, na última chamada, não há atualização na tabela do Funcionário, mesmo que o valor tenha sido alterado no meio do caminho, o valor final permanece o mesmo.

O Hibernate update

O Hibernate update deve ser usado quando sabemos que estamos apenas atualizando as informações da entidade. Esta operação adiciona o objeto da entidade ao contexto persistente e quaisquer alterações adicionais são rastreadas e salvas quando a transação é confirmada. Vamos verificar esse comportamento com um programa simples.

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

		// Trabalho de Preparação
		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();

		// exemplo de atualização
		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");

		// Fechar recursos
		sessionFactory.close();

	}

}

Ao executarmos o programa acima pela primeira vez, obtemos a seguinte saída.

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

Na execução subsequente, obtemos a seguinte saída.

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

Observe que não há atualizações disparadas após a primeira execução porque não há atualizações nos valores. Observe também que o nome do funcionário é “Nome atualizado final” que definimos após invocar o método update(). Isso confirma que o hibernate estava rastreando o objeto para quaisquer alterações e no momento de confirmar a transação, esse valor foi salvo.

Hibernate Merge

O hibernate merge pode ser usado para atualizar valores existentes, no entanto, este método cria uma cópia do objeto de entidade passado e o retorna. O objeto retornado faz parte do contexto persistente e é rastreado para quaisquer alterações, o objeto passado não é rastreado. Esta é a principal diferença com merge() de todos os outros métodos. Vamos ver isso com um programa simples.

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

		// Trabalho de Preparação
		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();

		 // exemplo de mesclagem - dados já presentes nas tabelas
		 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");

		// Fechar recursos
		sessionFactory.close();

	}

}

A saída na primeira execução é:

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

Na execução subsequente, a saída produzida é:

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

Observe que o objeto de entidade retornado por merge() é diferente da entidade passada. Também observe que, em execuções posteriores, o nome é “Kumar”, isso ocorre porque o objeto retornado é rastreado quanto a quaisquer alterações. Isso é tudo para os métodos Session do Hibernate save e update, espero que os exemplos acima o ajudem a esclarecer quaisquer dúvidas que você tenha.

Source:
https://www.digitalocean.com/community/tutorials/hibernate-session-merge-vs-update-save-saveorupdate-persist-example