Tuesday, July 24, 2007

Hibernate Querying Optimization - HQL with joins

The problem:


For each object that Hibernate loads, it needs to do one or more extra SQL queries to load associated objects. (Potentially as many as Kn + 1, where n is the number of objects returned).

Example:




  • Simplified hibernate mapping file

    <class name="Department">




    <many-to-one name="employee".../>




    </class>




  • Simple HQL Query

    from Department dept where dept.location like :LOCATION



  • The generated SQL statements:




  1. At least the initial select.

    select dept_id, deplt_name, dept_location...from department...




  2. Then a sequence of selects from the associated table. (n sql select statements)

    Select ...from employee...


    Select ...from employee...


    Select ...from employee...


    ...


    If Sale table returns 100 rows, you will see 100 select statements extra more than the main select statement.


The solution (1):


The association objects must be loaded in the initial query and just when you need them. This can be done by adding 'left join fetch' for example and switching the lazy loading on.

from Department dept left join fetch dept.employee


where dept.location like :LOCATION


This will generate just one sql select statement.

The solution (2): When there are a lot of association tables


You don't want to end up with query like this:

from Department dept


left join fetch dept.employee


left join fetch dept.manager


left join fetch dept.building


left join fetch dept.level


left join fetch dept.city


…etc


The approach involves determining exactly which columns you really need, and instantiating data-transfer objects containing exactly those columns.

Select new Department (dept.id, dept.employee.name, dept.manager.name, …) from Department dept




This technique is fast and efficient, and avoids the overheads of handling associated objects and of loading large numbers of persistent objects into the Hibernate session cache. The only downside is the need to create a dedicated data-transfer class for each query, and an overloaded constructor.



Reference: http://www.javalobby.org/articles/hibernate-query-101/

Inheritance Relation in Hibernate - Multiple join load problem

The Problem

Suppose we have class User which is the super class for all kind of Users, and we have these subclasses that inherit from User class:

ü EmployeeUser

ü StudentUser

ü TeacherUser

ü MerchantUser

ü VoucherUser

The User class is NOT abstract class, and many methods are implemented in it… keep in mind J

How to present this in Hibernate?

All of them will be presented in User.hbm.xml like this:

<class name="...User">

<id name="id"/>

<property ...

... All the properties in User class

<joined-subclass name="EmployeeUser">

<key column="ID" />

<property ...

... All the properties in EmployeeUser class

joined-subclass>

...All the other subclasses

Simple!!

After running the application, Hibernate will create the database schema... Nothing new Sami!!?? I know J

Let’s say now you want to implement the Login action for example, userName and password is in User class

What you will do is something like this:

//Code to check username and password that user entered in the login screen

Session session =

getSession();

Integer count = (Integer) session.createQuery("select count(*) from User where username = :NAME and password = :PASSWORD").setString("NAME", userName).setString("PASSWORD", password).uniqueResult();

Don’t tell me Sami that the login action is just 6 lines, impossible L

After doing this you will go to your team leader or Manager and tell him that you have done the project and you are ready to be team leader… Just Joking J

But unfortunately, the code you have written is a disaster… Yes, and you are fired out your company L

Look at the Hibernate generated query, you will see a very big query that you haven’t ever seen in your life, and it will be like this:

select count (*) from USER

inner join EMPLOYEE ON USER.ID= EMPLOYEE.ID-----------------1

inner join STUDENT ON USER.ID= STUDENT.ID-------------------2

inner join TEACHER ON USER.ID= TEACHER.ID-------------------3

inner join MERCHANT ON USER.ID= MERCHANT.ID-----------------4

inner join VOUCHER ON USER.ID= VOUCHER.ID-------------------5

Five inner joins for checking username and password that stored in the USER table... What you were thinking about when you wrote this?

This is a big problem, because if three users for example login at the same time, a very huge database load will fly!!

Solution

Using <discriminator> Hibernate tag

You can tell Hibernate that when you want a data for any subclass stored in the superclass, don’t “YA” Hibernate make any extra join that I don’t need, in other words: get the subclass from the superclass.

You will not change anything in your java code; all the changes will be in User.hbm.xml

Change hibernate configuration file to something like this after we take the User.hbm.xml for our example:

<class name="...User">

<id name="id"/>

<property ...

... All the properties in User class

<discriminator column="USER_TYPE" type="string" length="15" not-null="false" force="true"/>

<subclass name="EmployeeUser" discriminator-value="EMPLOYEE">

<join table="EMPLOYEE_USER" fetch="select">

<key column="ID" />

<property ...

... All the properties in EmployeeUser class

join>

subclass>

<subclass name="StudentUser" discriminator-value="STUDENT">

<join table="STUDENT_USER" fetch="select">

<key column="ID" />

<property ...

... All the properties in StudentUser class

join>

subclass>

...All the other subclasses and for each one of them don’t forget the discriminator-value

After doing this you have to restart the project and let Hibernate to alter the database to reflect the changes you made, and after that you and after running your big project (Login screen project) look at the hibernate sql generated query, it will be like this:

select count (*) from USER

where USER.USER_TYPE in (‘STUDENT’, ‘EMPLOYEE’, ‘TEACHER’, ‘MERCHANT’, ‘VOUCHER’) These are the discriminator-value for each subclass

No inner join at all J

Conclusion

One of the best practices in Hibernate says that:

When you have to implement inheritance tree in Hibernate, what is the kind of the superclass? What is the behavior of this superclass and its subclasses?

If the superclass is not abstract class, and many methods for subclasses are implemented in the superclass, and the superclass tasks are more than it subclasses tasks, then you have to use <discriminator> to get the subclasses from the superclass and decrease the database load.

Sunday, July 22, 2007

XML Schema-based configuration introduced in Spring 2.0

Problem
When the application server that runs our J2EE project disconnected from the internet, the project will not start and the exception below occurred:

org.xml.sax.SAXParseException: schema_reference.4: Failed to read schema document 'http://www.springframework.org/schema/beans/spring-beans-2.0.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not ...


Research
In our J2EE project, we used spring frame work to handle the transaction management for some classes like MockGMMManager for example

Spring frame work runs when the deployed project on the server initialized
Spring frame work Default behavior:

Spring frame work will read in some point this file mockApplicationContext.xml "For example" to load the MockGMMManager "For example" class in spring container

mockApplicationContext.xml
You will find this in teh file
...
DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
...

NOTE: The Spring frame work need’s XML schema definition file to read this xml file It will request this XSD from http://www.springframework.org/dtd/spring-beans-2.0.dtd and this is our problem!
When there is no internet connection, spring frame work will not be able to read this file and will throw org.xml.sax.SAXParseException


Solution
We should change this default behavior by locating the required XSD file locally from our server not remotely


1. Download the latest spring updates (spring-framework-2.0.6)
2. Put the new spring.jar in the project classpath
3. Create these files:
- \META-INF\spring.handlers
- \META-INF\spring.schemas
These files not required, but spring frame work will search for them in \META-INF\ folder by default to locate the required XSD’s and if they are not exist, it will locate them from the URL in mockApplicationContext.xml

"You can get a copy of spring.handlers and spring.schemas files in Spring-framework-2.0.6 package after you download it, just copy and paste"

Open spring.schemas you will find this:


...
http\://www.springframework.org/schema/beans/spring-beans-2.0.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd
...


This is one of the required XSD files spring-beans-2.0.xsd, if you open spring.jar you will see this file in org/springframework/beans/factory/xml/ The red text is the name for the schema location, and you will use in mockApplicationContext.xml

The rest of the file ...

http\://www.springframework.org/schema/tool/spring-tool-2.0.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd http\://www.springframework.org/schema/util/spring-util-2.0.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd http\://www.springframework.org/schema/aop/spring-aop-2.0.xsd=org/springframework/aop/config/spring-aop-2.0.xsd http\://www.springframework.org/schema/lang/spring-lang-2.0.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd http\://www.springframework.org/schema/tx/spring-tx-2.0.xsd=org/springframework/transaction/config/spring-tx-2.0.xsd http\://www.springframework.org/schema/jee/spring-jee-2.0.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-2.0.xsd http\://www.springframework.org/schema/tool/spring-tool.xsd=org/springframework/beans/factory/xml/spring-tool-2.0.xsd http\://www.springframework.org/schema/util/spring-util.xsd=org/springframework/beans/factory/xml/spring-util-2.0.xsd http\://www.springframework.org/schema/aop/spring-aop.xsd=org/springframework/aop/config/spring-aop-2.0.xsd http\://www.springframework.org/schema/lang/spring-lang.xsd=org/springframework/scripting/config/spring-lang-2.0.xsd http\://www.springframework.org/schema/tx/spring-tx.xsd=org/springframework/transaction/config/spring-tx-2.0.xsd http\://www.springframework.org/schema/jee/spring-jee.xsd=org/springframework/ejb/config/spring-jee-2.0.xsd


4. Change the mockApplicationContext.xml to be like this:

Remove this:

DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">

Put these inside the beans tag
xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"

4. Try to disconnect the server from the internet to test the solution
5. Restart the application server
6. Redeploy the project

You will not see the exception and the project will be started :-)