Hibernate - Elaborazione batch
Considera una situazione in cui devi caricare un gran numero di record nel tuo database utilizzando Hibernate. Di seguito è riportato lo snippet di codice per ottenere ciò utilizzando Hibernate -
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
}
tx.commit();
session.close();
Per impostazione predefinita, Hibernate memorizzerà nella cache tutti gli oggetti persistenti nella cache a livello di sessione e alla fine la tua applicazione cadrà con un OutOfMemoryExceptionda qualche parte intorno alla 50.000esima fila. Puoi risolvere questo problema, se stai usandobatch processing con Hibernate.
Per utilizzare la funzione di elaborazione batch, prima impostare hibernate.jdbc.batch_sizecome dimensione batch a un numero di 20 o 50 a seconda delle dimensioni dell'oggetto. Questo dirà al contenitore di ibernazione che ogni X righe deve essere inserita come batch. Per implementarlo nel tuo codice, avremmo bisogno di apportare piccole modifiche come segue:
Session session = SessionFactory.openSession();
Transaction tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
Employee employee = new Employee(.....);
session.save(employee);
if( i % 50 == 0 ) { // Same as the JDBC batch size
//flush a batch of inserts and release memory:
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Il codice sopra funzionerà correttamente per l'operazione INSERT, ma se si desidera eseguire l'operazione UPDATE, è possibile ottenere utilizzando il codice seguente:
Session session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE").scroll();
int count = 0;
while ( employeeCursor.next() ) {
Employee employee = (Employee) employeeCursor.get(0);
employee.updateEmployee();
seession.update(employee);
if ( ++count % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Esempio di elaborazione in batch
Modifichiamo il file di configurazione da aggiungere hibernate.jdbc.batch_size proprietà -
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-configuration SYSTEM
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name = "hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name = "hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<!-- Assume students is the database name -->
<property name = "hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<property name = "hibernate.connection.username">
root
</property>
<property name = "hibernate.connection.password">
root123
</property>
<property name = "hibernate.jdbc.batch_size">
50
</property>
<!-- List of XML mapping files -->
<mapping resource = "Employee.hbm.xml"/>
</session-factory>
</hibernate-configuration>
Considera la seguente classe POJO Employee:
public class Employee {
private int id;
private String firstName;
private String lastName;
private int salary;
public Employee() {}
public Employee(String fname, String lname, int salary) {
this.firstName = fname;
this.lastName = lname;
this.salary = salary;
}
public int getId() {
return id;
}
public void setId( int id ) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName( String first_name ) {
this.firstName = first_name;
}
public String getLastName() {
return lastName;
}
public void setLastName( String last_name ) {
this.lastName = last_name;
}
public int getSalary() {
return salary;
}
public void setSalary( int salary ) {
this.salary = salary;
}
}
Creiamo la seguente tabella EMPLOYEE per memorizzare gli oggetti Employee:
create table EMPLOYEE (
id INT NOT NULL auto_increment,
first_name VARCHAR(20) default NULL,
last_name VARCHAR(20) default NULL,
salary INT default NULL,
PRIMARY KEY (id)
);
Di seguito sarà il file di mappatura per mappare gli oggetti Employee con la tabella EMPLOYEE -
<?xml version = "1.0" encoding = "utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name = "Employee" table = "EMPLOYEE">
<meta attribute = "class-description">
This class contains the employee detail.
</meta>
<id name = "id" type = "int" column = "id">
<generator class="native"/>
</id>
<property name = "firstName" column = "first_name" type = "string"/>
<property name = "lastName" column = "last_name" type = "string"/>
<property name = "salary" column = "salary" type = "int"/>
</class>
</hibernate-mapping>
Infine, creeremo la nostra classe applicativa con il metodo main () per eseguire l'applicazione che useremo flush() e clear() metodi disponibili con l'oggetto Session in modo che Hibernate continui a scrivere questi record nel database invece di memorizzarli nella cache.
import java.util.*;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
public class ManageEmployee {
private static SessionFactory factory;
public static void main(String[] args) {
try {
factory = new Configuration().configure().buildSessionFactory();
} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}
ManageEmployee ME = new ManageEmployee();
/* Add employee records in batches */
ME.addEmployees( );
}
/* Method to create employee records in batches */
public void addEmployees( ){
Session session = factory.openSession();
Transaction tx = null;
Integer employeeID = null;
try {
tx = session.beginTransaction();
for ( int i=0; i<100000; i++ ) {
String fname = "First Name " + i;
String lname = "Last Name " + i;
Integer salary = i;
Employee employee = new Employee(fname, lname, salary);
session.save(employee);
if( i % 50 == 0 ) {
session.flush();
session.clear();
}
}
tx.commit();
} catch (HibernateException e) {
if (tx!=null) tx.rollback();
e.printStackTrace();
} finally {
session.close();
}
return ;
}
}
Compilazione ed esecuzione
Ecco i passaggi per compilare ed eseguire l'applicazione sopra menzionata. Assicurati di aver impostato PATH e CLASSPATH in modo appropriato prima di procedere per la compilazione e l'esecuzione.
Crea il file di configurazione hibernate.cfg.xml come spiegato sopra.
Crea il file di mappatura Employee.hbm.xml come mostrato sopra.
Crea il file sorgente Employee.java come mostrato sopra e compilarlo.
Crea il file sorgente ManageEmployee.java come mostrato sopra e compilarlo.
Esegui il binario ManageEmployee per eseguire il programma, che creerà 100000 record nella tabella EMPLOYEE.