Wednesday, July 23, 2008

Inheritance Strategy - Class per hierarchy

Hibernate supports several types of inheritance strategies in which simplest of them is "Class-per-heirarchy". In this strategy, data for all classes involved in inheritance is stored in a single table and a record is identified by discriminator column. Lets discuss this strategy with an example. Suppose there are three classes: Person, Employee, and Physician. Inheritance relationship is shown in the fig below:Person is an abstract class. So either a person will exist as Employee or Physician. Hibernate mapping for Person is shown below:


Discriminator element creates a column with name "Type", value of which will indicate whether a particular record is Employee or Physician. There is also a special property "abstract" in class element which tells that object of person can't be created.

Hibernate mapping for Employee is shown below:



"subclass" element indicates that there is a class per hierarchy strategy of inheritance between Employee and Person. "extends" property of subclass element creates relationship of "Employee" with "Person". "discriminator-value" tells hibernate to use "Employee" in discriminator column to identify all employees.

Configuration for Physician is given below:



Now lets add two Persons: one is Employee and other is Physician as shown below:

Session session = HibernateUtil.getSessionFactory().openSession(); session.beginTransaction(); Emplo
yee emp = new Employee(); emp.setName("Amer Sohail"); emp.setPay(123f); session.save(emp); Physician phy = new Physician(); phy.setName("Dr. Amer Sohail"); phy.setPay(345f);
phy.setBillingAddress("Lahore");
phy.setSpeciality("Pediatrics");
session.save(phy); session.getTransaction().commit(); session.close();


a single table with name "person" will be created in the database with following two records:


Lets retrieve the all person from database and you will observe polymorphic feature of hibernate:

Session session = HibernateUtil.getSessionFactory().openSession();
session.beginTransaction();
List emp = session.createQuery("from Person emp").list();
if (emp != null && emp.size() > 0)
{
for (int index = 0; index <>
{
System.out.println("Name of employee is " +((Employee)emp.get(index)).getName());
if (emp.get(index) instanceof Person)
System.out.println("This is person");

if (emp.get(index) instanceof Employee)
System.out.println("This is Employee");

if (emp.get(index) instanceof Physician)
System.out.println("This is Physician");
}
}
session.getTransaction().commit();
session.close();


output of the code will be following:

Name of employee is Amer Sohail
This is person
This is Employee
Name of employee is Dr. Amer Sohail
This is person
This is Employee
This is Physician

As shown in output, i just retrieved "persons" but it instantiated objects of type to which they belong e.g. Amer Sohail was just an employee not a physician so hibernate created it of "Employee" type. This is really a very powerful feature of hibernate.

Now if i want to retrieve only employee not physician, then i will write the following query:


List emp = session.createQuery("from Employee emp where type = Employee").list();


I explained the strategy "class-per-heirarchy" in this blog's post. This strategy has one major drawback which you should notice while viewing records in table. It contains "nulls" against those records which are not in employee like "speciality" and "billing_address". Since DBMS has to handle null fields with a special case and it creates an impact on performance so that is the major drawback of this strategy because it will create a lot of nulls in the table. but on the other handle, no join is needed so no join cost improves the performance. Now there is very interesting situation bcz pros of this blog says it increase the performance while cons says it decreases the performance so what is the net effect. you have to wait for it for later posts in which i will compare all the strategies.

Saturday, July 19, 2008

Batch Insertion in Hibernate

Batch Insertion is a powerful feature of hibernate particularly useful when you are importing data from other systems in batch. If you do not use batch feature of hibernate, your application's performance may decrease dramatically at the time of insertion of many records.
There are two approaches in hibernate to handle batch insertion. Each one is explained below:

  • One approach is related to Session class. Let's discuss it with example. Suppose there is an application for library which uses hibernate as ORM layer. Now you want to insert 1 million books in library. For simplicity domain model contains two classes one is book and second one is publisher. Publisher is contained in book class and i have enabled cascade insert and cascade update so if i insert book which contains publisher that is not in the system, it will also be saved with book. Code to insert books are given below:

    Session session =HibernateUtil.getSessionFactory().openSession(); session.beginTransaction();
    for (int index = 0; index <> Book book = new Book();
    book.setAuthor("amer");
    book.setIsbn("34343");
    book.setName("Hibernate " + index);
    Publisher pub = new Publisher();
    pub.setName("Publisher " + index);
    book.setPublisher(pub);
    book.setPublishDate(new Date());
    session.save(book);
    }
    session.getTransaction().commit();
    session.close();


Above code is putting a lot of memory burden because whenever an object is saved, hibernate puts that object in cache which is called as "session cache" or "first level of cache" and probably you will face an error of Stack outofflow memory error. In order to avoid this, you have to clear the session, but question is when session should be cleared either after each insertion of after some interval. If you clear session, after each insertion, it will decrease your applications' performance dramatically because before calling clear operation on session, you have to call flush operation which will synchronize persistent data store to objects memory states. For this situation, there comes a concept of batch insertion. For batch insertion, first of all you will have to add "hibernate.jdbc.batch_size" property in your hibernate.cfg.xml file with value of 50.

By using this property, hibernate will use jdbc for batch insertion of 50 records at a time when you flush the session. Batch size "50" is my recommendation because i always got max application performance with this number. You also have to change code to flush objects from session as below:

session.save(book); if (index % 50== 0) { session.flush(); session.clear(); }

  • Second way of doing batch insertion is through "StatelessSession" class. StatelessSession class differs from Session class in that it does not cache the objects, does not call interceptors, does not save any persistence context of object, does not cascade to composed objects, does not take care of collections, directly transfers the object to jdbc insert statement. So in other way it is more close to jdbc. With StatelessSession, you have to save composed objects separately e.g. in above example, you have to insert publisher and books separately. Above code with StatelessSession will be:


    StatelessSession session = HibernateUtil.getSessionFacgtory().openStatelessSession(); session.beginTransaction();
    for (int index = 0; index < style="font-style: italic; color: rgb(255, 153, 102);"> {
    Book book = new Book();
    book.setAuthor("amer");
    book.setIsbn("34343");
    book.setName("Hibernate " + index);
    Publisher pub = new Publisher();
    pub.setName("Publisher " + index);
    book.setPublisher(pub);
    book.setPublishDate(new Date());
    session.insert(pub);
    session.insert(book);
    }
    session.getTransaction().commit();
    session.close();

Well its time now to conclude the things. I have used both ways and i prefer first approach over second one because of the following reasons:
  • StatelessSession does not provide any performance over Session when you have to save composed instances too.
  • It does not call any interceptors or evens which may complicate the application design if we heavily depend on interceptors or events for logging or data level security etc.
  • Even for a plain object, StatelessSession does not provide me much performance over Session. It only provided me performance when an object contains one or two attributes. e.g. if i only insert publisher through StatelessSession, i will get performance by 20 to 30 seconds.

Saturday, July 5, 2008

Packaging j2se application in jar

Jar file is distributable unit for j2se applications. It is a zipped file but with jar extension. Jar file contains following contents:

- Java compiled classes
- manifest.mf file which contains meta info regarding jar like name of main class, jars to be added in classpath etc, version info etc. (optional)
- other jars on which classes of this jar depends (optional)

There are two types of jars:

- Library Jar
- Executable Jar

Difference between the two jars is simply a main class. If your application runs standalone and contains main class, then you put name of main class in manifest file. This makes a jar an executable jar. You can run your executable jar just by double clicking on it or through java command with switch -jar. On the other hand, library jar does not contain any main class, and it always runs as a dependency jar for other java applications.

Now lets see how we can create a library jar
If you project just contains java classes and no other jars or manifest file, then you will create jar using following command:

project-folder>jar cvf mylibrary.jar -C classes .

cvf are three switches for jar. "c" means i want to create a new jar rather than updating previous one. "v" means i want to print all log info on console. "f" means supply an output to file i.e. mylibrary.jar in this case. -C is another switch which is used to tell which folders you want to include in jar. In my case all compiled java files are under classes folder but i don't want to include classes folder itself in jar so i used "-C classes .". Dot "." here tells jar command not to include classes folder. If you omit ".", then you have to change switch -C with this "-C classes/". it will tell jar command that all compiles files are under classes folder and include classes folder itself in jar file.
if you look at the contents of created jar file using any unzipped software, you will find that it contains classes + one more folder named as "META-INF". This folder contains manifest.mf. By default jar creates manifest.mf itself if you don't provide it. If you open this manfiest.mf using any text editor, you will see it just contains following two entries:

Manifest-Version: 1.0
Created-By: 1.6.0_03 (Sun Microsystems Inc.)

Important one is "Created-By" which tells which version of java was used to package jar file. In this case it is jse 6 with update 3.
If classes depend on other jars, you have to include those jars in classpath. This can be done at the time of running a program or writing all dependencis in manifest file, so java will automatically add it to classpath. Suppose above jar "mylibrary.jar" depends on other jars named as "other1.jar" and "other2.jar". Now to include these two jars in classpath through manifest file. Create a text file with any name let dependencies.txt and add following lines:

Class-Path: other1.jar other2.jar

Keep in mind that you have to provide spaced in between the every two jar file names.

Now you can add this manifest file to already created manifest file using following command:

project-folder> jar uvfm mylibrary.jar dependencies.txt

Console output will show that Manifest.mf updated. In this command i used switch "u" instead of "c" because i want to append manifest in already created jar. I also used "m" switch, which is used when you want to provide your own manifest file. If i want to create new jars with my own manifest file, then i have to use following command:

project-folder> jar cvfm mylibrary.jar dependencies.txt -C classes .

It is the same command as used previously to create jar with exception of extra "m" switch and name of manifest file.

Procedure of creating executable jar file is the same as library jar but you have to provide one extra attribute in your manifest file with name "Main-Class", So if i have name of main class as "org.engineers.java.MyMain", then i will have to added one entry in manifest file as:

Main-Class: org.engineers.java.MyMain