Tuesday, November 28, 2017

Hibernate 3

Cascade in Hibernate

1. inverse: This is used to decide which side is the relationship owner to manage the relationship (insert or update of the foreign key column).
2. cascade: In cascade, after one operation (save, update and delete) is done, it decide whether it need to call other operations (save, update and delete) on another entities which has relationship with each other.
Conclusion: In short, the “inverse” is decide which side will update the foreign key, while “cascade” is decide what’s the follow by operation should execute. Both are look quite similar in relationship, but it’s totally two different things. Hibernate developers are worth to spend time to research on it, because misunderstand the concept or misuse it will bring serious performance or data integrity issue in your application.

Cascading in Hibernate has many options like save-update, persist,merge, delete etc. Cascade='all' is a way of saying apply all cascading options.




---------------------------------------------------------------------------------------------------------------------------
Hibernate Transaction Management Example
2.     ACID Property
transaction simply represents a unit of work. In such case, if one step fails, the whole transaction fails (which is termed as atomicity). A transaction can be described by ACID properties (Atomicity, Consistency, Isolation and Durability).
1.2.1        Transaction Interface in Hibernate
In hibernate framework, we have Transaction interface that defines the unit of work. It maintains abstraction from the transaction implementation (JTA,JDBC).
A transaction is associated with Session and instantiated by calling session.beginTransaction().
The methods of Transaction interface are as follows:
1.     void begin() starts a new transaction.
2.     void commit() ends the unit of work unless we are in FlushMode.NEVER.
3.     void rollback() forces this transaction to rollback.
4.     void setTimeout(int seconds) it sets a transaction timeout for any transaction started by a subsequent call to begin on this instance.
5.     boolean isAlive() checks if the transaction is still alive.
6.     void registerSynchronization(Synchronization s) registers a user synchronization callback for this transaction.
7.     boolean wasCommited() checks if the transaction is commited successfully.
8.     boolean wasRolledBack() checks if the transaction is rolledback successfully.


Example of Transaction Management in Hibernate
In hibernate, it is better to rollback the transaction if any exception occurs, so that resources can be free. Let's see the example of transaction management in hibernate.
1.      Session session = null;  
2.      Transaction tx = null;  
3.        
4.      try {  
5.      session = sessionFactory.openSession();  
6.      tx = session.beginTransaction();  
7.      //some action  
8.        
9.      tx.commit();  
10.    
11.  }catch (Exception ex) {  
12.  ex.printStackTrace();  
13.  tx.rollback();  
14.  }  
15.  finally {session.close();}  





------------------------------------------------------------------------------------------------------------------------

5) What is SessionFactory?
SessionFactory provides the instance of Session. It is a factory of Session. It holds the data of second level cache that is not enabled by default.
 6) Is SessionFactory a thread-safe object?
Yes, SessionFactory is a thread-safe object, many threads can access it simultaneously.


Batch Processing
Consider a situation when you need to upload a large number of records into your database using Hibernate. Following is the code snippet to achieve this using Hibernate:
Sessionsession=SessionFactory.openSession();
Transactiontx=session.beginTransaction();
for(inti=0;i<100000;i++){
Employeeemployee=newEmployee(.....);
session.save(employee);
}
tx.commit();
session.close();
Because by default, Hibernate will cache all the persisted objects in the session-level cache and ultimately your application would fall over with anOutOfMemoryException somewhere around the 50,000th row. You can resolve this problem if you are using batch processing with Hibernate.
To use the batch processing feature, first set hibernate.jdbc.batch_size as batch size to a number either at 20 or 50 depending on object size. This will tell the hibernate container that every X rows to be inserted as batch. To implement this in your code we would need to do little modification as follows:
Session session=SessionFactory.openSession();
Transaction tx=session.beginTransaction();
for(inti=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();
Above code will work fine for the INSERT operation, but if you are willing to make UPDATE operation then you can achieve using the following code:
Sessionsession=sessionFactory.openSession();
Transactiontx=session.beginTransaction();

ScrollableResultsemployeeCursor=session.createQuery("FROM EMPLOYEE")
.scroll();
int count =0;

while(employeeCursor.next()){
Employeeemployee=(Employee)employeeCursor.get(0);
employee.updateEmployee();
seession.update(employee);
if(++count %50==0){
session.flush();
session.clear();
}
}
tx.commit();
session.close();
Batch Processing Example:
Let us modify configuration file as to add hibernate.jdbc.batch_sizeproperty:
<?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>
<propertyname="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<propertyname="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>

<!-- Assume students is the database name -->
<propertyname="hibernate.connection.url">
jdbc:mysql://localhost/test
</property>
<propertyname="hibernate.connection.username">
      root
</property>
<propertyname="hibernate.connection.password">
      root123
</property>
<propertyname="hibernate.jdbc.batch_size">
      50
</property>

<!-- List of XML mapping files -->
<mappingresource="Employee.hbm.xml"/>

</session-factory>
</hibernate-configuration>
Consider the following POJO Employee class:
publicclassEmployee{
privateint id;
privateStringfirstName;
privateStringlastName;
privateint salary;

publicEmployee(){}
publicEmployee(Stringfname,Stringlname,int salary){
this.firstName=fname;
this.lastName=lname;
this.salary= salary;
}
publicintgetId(){
return id;
}
publicvoidsetId(int id ){
this.id = id;
}
publicStringgetFirstName(){
returnfirstName;
}
publicvoidsetFirstName(Stringfirst_name){
this.firstName=first_name;
}
publicStringgetLastName(){
returnlastName;
}
publicvoidsetLastName(Stringlast_name){
this.lastName=last_name;
}
publicintgetSalary(){
return salary;
}
publicvoidsetSalary(int salary ){
this.salary= salary;
}
}
Let us create the following EMPLOYEE table to store Employee objects:
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)
);
Following will be mapping file to map Employee objects with EMPLOYEE table.
<?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>
<classname="Employee"table="EMPLOYEE">
<metaattribute="class-description">
         This class contains the employee detail.
</meta>
<idname="id"type="int"column="id">
<generatorclass="native"/>
</id>
<propertyname="firstName"column="first_name"type="string"/>
<propertyname="lastName"column="last_name"type="string"/>
<propertyname="salary"column="salary"type="int"/>
</class>
</hibernate-mapping>
Finally, we will create our application class with the main() method to run the application where we will use flush() and clear() methods available with Session object so that Hibernate keep writing these records into the database instead of caching them in the memory.
importjava.util.*;

importorg.hibernate.HibernateException;
importorg.hibernate.Session;
importorg.hibernate.Transaction;
importorg.hibernate.SessionFactory;
importorg.hibernate.cfg.Configuration;

publicclassManageEmployee{
privatestaticSessionFactory factory;
publicstaticvoid main(String[]args){
try{
         factory =newConfiguration().configure().buildSessionFactory();
}catch(Throwable ex){
System.err.println("Failed to create sessionFactory object."+ ex);
thrownewExceptionInInitializerError(ex);
}
ManageEmployee ME =newManageEmployee();

/* Add employee records in batches */
ME.addEmployees();
}
/* Method to create employee records in batches */
publicvoidaddEmployees(){
Sessionsession=factory.openSession();
Transactiontx=null;
IntegeremployeeID=null;
try{
tx=session.beginTransaction();
for(inti=0;i<100000;i++){
Stringfname="First Name "+i;
Stringlname="Last Name "+i;
Integer salary =i;
Employeeemployee=newEmployee(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;
}
}
Compilation and Execution:
Here are the steps to compile and run the above mentioned application. Make sure you have set PATH and CLASSPATH appropriately before proceeding for the compilation and execution.
·        Create hibernate.cfg.xml configuration file as explained above.
·        Create Employee.hbm.xml mapping file as shown above.
·        Create Employee.java source file as shown above and compile it.
·        Create ManageEmployee.java source file as shown above and compile it.
·        Execute ManageEmployee binary to run the program which will create 100000 records in EMPLOYEE table.

Hibernate JPA Annotations - Contents:

Annotation
Package Detail/Import statement
import javax.persistence.Entity;
import javax.persistence.Table;
import javax.persistence.Column;
import javax.persistence.Id;
import javax.persistence.GeneratedValue;
import javax.persistence.Version;
import javax.persistence.OrderBy;
import javax.persistence.Transient;
import javax.persistence.Lob;
import javax.persistence.OneToOne;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.ManyToMany;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.MapsId;
import javax.persistence.Inheritance;
import javax.persistence.DiscriminatorColumn;
import javax.persistence.DiscriminatorValue;
@Entity
Annotate all your entity beans with @Entity. 


@Entity
public class Company implements Serializable {
...
}
@Table
Specify the database table this Entity maps to using the name attribute of @Table annotation. In the example below, the data will be stored in 'company' table in the database. 



@Entity
@Table(name = "company")
public class Company implements Serializable {
...
}



@Column
Specify the column mapping using @Column annotation.
@Entity
@Table(name = "company")
public class Company implements Serializable {

  @Column(name = "name")
  private String name;
   
...
}


@Id
Annotate the id column using @Id.
@Entity
@Table(name = "company")
public class Company implements Serializable {

  @Id
  @Column(name = "id")
  private int id;
   
...
}
@GeneratedValue
Let database generate (auto-increment) the id column.
@Entity
@Table(name = "company")
public class Company implements Serializable {

  @Id
  @Column(name = "id")
  @GeneratedValue
  private int id;
   
...
}
@Version
Control versioning or concurrency using @Version annotation.
@Entity
@Table(name = "company")
public class Company implements Serializable {

  @Version
  @Column(name = "version")
  private Date version;
   
...
}
@OrderBy
Sort your data using @OrderBy annotation. In example below, it will sort all contacts in a company by their firstname in ascending order.
1
2
@OrderBy("firstName asc")
private Set contacts;
1.1.1        @Transient
Annotate your transient properties with @Transient.
1.1.2        @Lob
Annotate large objects with @Lob.
1.2         Hibernate Association Mapping Annotations
1.2.1        Example App DB Schema
 
The database for this tutorial is designed to illustrate various association mapping concepts. 
In RDBMS implementations, entities are joined using the following ways:
  • Shared Primary Key
  • Foreign Key
  • Association Table
In our example app,
  • Tables company and companyDetail have shared values for primary key. It is a one-to-one assoication.
  • Tables contact and contactDetail are linked through a foreign key. It is also a one to one association.
  • Tables contact and company are linked through a foriegn key in many-to-one association with contact being the owner.
  • Tables company and companyStatus are linked through a foreign key in many-to-one association with company being the owner.
@OneToOne
  • Use @PrimaryKeyJoinColumn for associated entities sharing the same primary key.
  • Use @JoinColumn & @OneToOne mappedBy attribute when foreign key is held by one of the entities.
  • Use @JoinTable and mappedBy entities linked through an association table.
  • Persist two entities with shared key using @MapsId
For entities Company and CompanyDetail sharing the same primary key, we can associate them using @OneToOne and @PrimaryKeyJoinColumn as shown in the example below. 

Notice that the id property of CompanyDetail is NOT annotated with @GeneratedValue. It will be populated by id value of Company.
@Entity
@Table(name = "company")
public class Company implements Serializable {
   
  @Id
  @Column(name = "id")
  @GeneratedValue
  private int id;
   
  @OneToOne(cascade = CascadeType.MERGE)
  @PrimaryKeyJoinColumn
  private CompanyDetail companyDetail;
   
  ...
}

@Entity
@Table(name = "companyDetail")
public class CompanyDetail implements Serializable {

  @Id
  @Column(name = "id")
  private int id;
   
  ...
}


No comments:

Post a Comment