Thursday, 3 April 2008

Hibernate Cascading for Save and Delete

To explain the cascading of save and delete lets take an example of Employee, Company and Acccount.
Many employees can belong to same company.
An employee can have many accounts with it.
Company.java
package model;
import javax.persistence.*;
import javax.persistence.Entity;
import java.util.List;
@Entity
public class Company {
@Id
private long id;
private String name;
@OneToMany(mappedBy = "company")
private List employees;
public List getEmployees() {
return employees;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setEmployees(List employees) {
this.employees = employees;
}
}
Employee.java
package model;
import org.hibernate.annotations.*;
import javax.persistence.*;
import javax.persistence.Entity;
import java.util.List;
import java.util.ArrayList;
@Entity
public class Employee {
@Id
private long id;
private String name;
@ManyToOne
@JoinColumn(name = "company_id")
@Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE})

private Company company;
@OneToMany(mappedBy = "employee")
@Cascade({org.hibernate.annotations.CascadeType.ALL})

private List accounts;
public Company getCompany() {
return company;
}
public void setCompany(Company company) {
this.company = company;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public List getAccounts() {
return accounts;
}
public void setAccounts(List accounts) {
this.accounts = accounts;
}
public void addAccount(Account account){
if(getAccounts()==null){
accounts=new ArrayList();
}
getAccounts().add(account);
}
}
Account.java
package model;
import javax.persistence.*;
import javax.persistence.Entity;
@Entity
public class Account {
@Id
private long id;
private String name;
@ManyToOne
private Employee employee;

public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Employee getEmployee() {
return employee;
}
public void setEmployee(Employee employee) {
this.employee = employee;
}
}
Save Cascading
Cascading Save is a recursive process that could be described as "walking the object graph". The general algorithm for save goes:
· Prior to saving a bean we save its ManyToOne associations (And appropriate OneToOnes)
· Then we save the bean itself
· Then we save any OneToMany, ManyToMany (And appropriate OneToOnes)
Note: OneToOnes will occur prior and post saving the bean depending on whether the OneToOne owns the foreign key or not. If that doesn't make sense don't worry too much.
Employee employee=new Employee();
………………………..
//employee is belonging to one company.
//employee has two accounts (assume two).
Session.save(employee);
In this example
Session.save(employee) will do the following things.
Prior to saving the employee, company will be saved.
After the company is saved the employee will be saved.
After the employee is saved the accounts are saved.
A "Relational" way of describing this is that we go "up the imported foreign keys", then save the bean, then go "down the exported foreign keys".
Perhaps another way of thinking about this is to take the example where every object is new and will need to be inserted.
Before we save the employee, we need company’s id, hence company is saved before employee.
Once the company is saved, employee is saved.
Accounts need employee_id before it is saved.
Hence once, employee is saved, accounts are also saved.
Delete Cascading
Delete cascading is very similar except the order is reversed.
1 Prior to deleting a bean we delete its OneToMany, ManyToMany (And appropriate OneToOnes) associations
2 Then we delete the bean itself
3 Then we delte any ManyToOne (And appropriate OneToOnes)

Here is the test class which uses these three classes to create record in database.
package test;
import org.hibernate.SessionFactory;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import model.Employee;
import model.Company;
import model.Account;
public class MyTest {
public static void main(String[] args) {
Resource resource=new ClassPathResource("applicationContext.xml");
BeanFactory factory=new XmlBeanFactory(resource);
SessionFactory sessionFactory= (SessionFactory) factory.getBean("sessionFactory");
Session session=sessionFactory.openSession();
Transaction transaction=session.beginTransaction();
Employee employee=new Employee();
employee.setId(1);
employee.setName("abcd");
Company company=new Company();
company.setId(1);
company.setName("ABCDEFGH");
employee.setCompany(company);
Account account1=new Account();
account1.setId(1);
account1.setName("HDFC");
account1.setEmployee(employee);
Account accoutn2=new Account();
accoutn2.setId(2);
accoutn2.setName("ICICI");
accoutn2.setEmployee(employee);
employee.addAccount(account1);
employee.addAccount(accoutn2);
session.save(employee);
transaction.commit();
session.flush();
session.close();
}
}

Saturday, 22 March 2008

Writing ajax code and display the result of ajax in a div

Here a code for ajax request to server.

This code defines a function sendRequest(url,divId).
This function takes two arguments, first - the url to which ajax request is to be made. second - the divId in which the response is to be included.

<script>
var http = getHTTPObject();
function sendRequest(url,divId) {
SubmitRBData(url,divId);
}

function SubmitRBData(strUrl,divId) {
http.open("GET", strUrl, true);
http.onreadystatechange = function handleHttpResponse() {

if (http.readyState == 4) {
if (http.status == 200){
results = http.responseText;
document.getElementById(divId).innerHTML=results; // string response
}}};
http.send(null);
}

function getHTTPObject() {
var objXMLHttp=null;
if (window.XMLHttpRequest){
objXMLHttp=new XMLHttpRequest();
}
else if (window.ActiveXObject){
objXMLHttp=new ActiveXObject("Microsoft.XMLHTTP");
}
return objXMLHttp;
}
</script>


To make a ajax call, simply include this javascript in your code and call the sendRequest method.

Thursday, 20 March 2008

What is OpenSessionInViewFilter and how to use it?

Recently, in one of our project we were using hibernate. We were having hibernate integrated with Velocity, Webwork and Spring. We wanted to show a webpage where the webwork action will make a hibernate query and get the fields from one table. example : We were having a database schema to store countries and cities. The cities were mapped to country. Now our webpage should show all the countries and all the cities within that country. Our hibernate objects were created in such a way that we could retrieve cities from country(One-To-Many). We have also made LAZY loading for cities from country. i.e a country should return cities only when asked. or in other words, the hibernate wont query the database for cities in particular country unless asked.
Since we were using LAZY loading, we faced a problem.
When user sends a request to view page, our webwork action method is called. In this action method we load all the countries, but since cities are lazily loaded, no cities are loaded in action method. In the same action method we open the sesison and query for country and then close the session. Now list of cities is returned to the velocity Parser, but the response is not yet rendered. The velocity page now tries to fetch the cities in each country. Since session is already closed the getter method for cities gives LAZY Initialization Exception. This is because our action method made a query for country and closed the session. When velocity asks for cities and hibernate tries to query for cities, it finds the session to be closed. Hence LAZY initialization error.

This problem can be solved if we open a session on client request and close it before sending a response back to client. This can be achieved with Spring's OpenSessionInViewFilter.

To use OpenSessionInViewFilter, edit your web.xml file and put this filter before webwork filter.

<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>org.springframework.orm.hibernate.support.OpenSess ionInViewFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


Issues: Now when you are using OpenSessionInViewFilter, by default the session's flush mode is set to NEVER. So when you try to save something in your action using hibenate and commit it, it wont be reflected in your database. To solve this you need to flush the session in your action class or extend OpenSessionInViewFilter and override closeSession(Session session, SessionFactory sessionFactory).
public MyOSIVFilter extends OpenSessionInViewFilter{
public void closeSession(Session session, SessionFactory sessionFactory){
session.flush();
super.closeSession(session,sessionFactory);
}
}


change your web.xml to
<filter>
<filter-name>hibernateFilter</filter-name>
<filter-class>package.MYOSIVFilter</filter-class>
</filter>

<filter-mapping>
<filter-name>hibernateFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


Now it is also possible that you are maintaining a single transaction for per request. In your action you edit the attributes of a object and update it using session.update(object). But it is not yet commited as some other processing is remaining. At the same time, some other request is invoking a action which tries to retrieve the object which you were updating. Since the object is not yet commited the other request will get the old object. To solve this you need to begin a transaction before you load object and commit the transaction after you update the object. So that as soon as the object is saved/updated it is commited. With this there can be many transaction in single user request but only one session.

Now there is another way to do the same thing using Spring TransactionProxyFactoryBean.
This is actually a proxy factory which takes the target object which needs to be proxied, the method to be proxied and the transactionManager.

below is the code snippet from applicationContext.xml

<bean id="repository" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="hibernateTransactionManager"/>
<property name="transactionAttributeSource" ref="transactionAttributeSource"/>
<property name="target" ref="daoImpl"/>
<property name="proxyInterfaces">
<list>
<value>package.DAO</value>
</list>
</property>
</bean>

<bean id="transactionAttributeSource"
class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource">
<property name="properties">
<value>
get*=PROPAGATION_REQUIRED,readOnly
find*=PROPAGATION_REQUIRED,readOnly
create*=PROPAGATION_REQUIRED,readOnly
delete*=PROPAGATION_REQUIRED
</value>
</property>
</bean>

<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" >
.........................
.............
.....
......
</bean>

<bean id="hibernateTransactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>

<bean id="daoImpl" class="package.daoImpl">
...................................
..................................
</bean>


Above code says the TransactionProxyFactoryBean that all the methods in daoImpl starting with get, find, create, delete should be proxied and for all this methods the transaction should be begin before method starts and should get commited after method finishes.

Saturday, 15 March 2008

What are the other alternatives for session.load in hibernate to get single object(row) from database?

Generally when we want to delete any row from database using hibernate, we first load the object using session.load(Class,Long). We then delete the object retrieved for id using session.remove(object).

The problem with this approach is that Hibernate first fires a select query to the database to get the object(row). After it gets the object hibernate then fires a delete query to delete the row. So, do one operation on database, we are making hibernate to fire two queries.

Also another problem associated with this approach is that if the object of class(Table) which we want to delete may have join to other tables. In such scenario, when we are loading the object using session.load the join query is fired which is heavy.

The ideal approach would be create a hibernate query for deleting the object(row). In this approach hibernate will only fire single delete query. As only one query if fired, the time taken taken would be less.

eg:

Normal approach to delete using hibernate
MyTable table= (MyTable)session.load(MYTable.class,new Long(2);
session.remove(table);
session.flush();
session.close();

IDeal Approach to delete single row using hibernate
session.createQuery("delete from MyTable where id=2").executeUpdate();
session.fluch();
session.close();

Friday, 14 March 2008

How to search indexes using lucene?

To search through indexes using your java code is easy. You can search through your indexes created by lucene using lucene API. Lucene-core-2.2.0.jar.

Following is the code usage showing how to search through lucene index.

IndexReader reader = null;

reader = IndexReader.open("C:\\var\\lucene\\indexes\\Document"); (path of your lucene index i.e. segments.gen)

Searcher searcher = new IndexSearcher(reader);

Analyzer analyzer = new StandardAnalyzer();

MultiFieldQueryParser parser = new MultiFieldQueryParser(new String[]{"content", "title", "description"}, analyzer); ('content','title','description' are the fields which we want to search in index)

Query query = parser.parse('searchString');

Hits hits = searcher.search(query);

Once you get the hits object you can find any data that is saved in index using the hits object.

Thursday, 13 March 2008

How to write a servlet that downloads the file saved as blob in database?

When we store any file in database in the blob column, we would also want it to be downloaded as and when required by webpage.

To achieve this we can write a servlet or a action in Webwork that makes this facility available. In the servlet we write the jdbc code to get the blob column in bytes[] format. Once we have file content in bytes[] we can add it to the response using OutputStream. Also if we want to give a name to file we can set the header in the response. Following is the code which will show how to do this.

----------- jdbc code to get the blob column ---------

bytes[] content= //get the blob using jdbc or any persistent framework.

doService(ServletRequest req, ServletResponse res){

HttpServletResponse response=(HttpServletResponse)res ;

response.setContenType("application/doc");

response.addHeader("Content-Disposition", "attachment; filename=file.doc"

ServletOutputStream output = response.getOutputStream();

output.write(content);

}

Monday, 10 March 2008

Problem with querying table which has blob field and the query returns large rows (Using Hibernate and Lucene)

We were having a issue with one of our design. We were having a table which used to store entire file in blob column. When we query a table with some criteria and if the query returns large number of results or the rows and each row containing large blob, we were getting Out of Memory error in Java. To solve this we made a slight modification to our design of tables.

We created two tables file and filecontent and mapped them by one-to-one mapping using hibernate. The blob column was now shifted to filecontent table. We were using lucene to index the content of file. Since blob column was now moved to filecontent table, the indexing which we did on file table didn’t work. To solve this, we used @EmbeddedObject annotation in file object. Our lucene indexing problem was now solved. Indexes were getting created, but now the same problem occurred again. The hibernate-lucene in turn does a join query when we search through index. As a result again the huge blob is getting retrieved along with other columns. To solve this we again changed our design, but this time only in hibernate configuration.

We created a transient field of type byte[] in file object and made it indexable. So we were having the file’s content in two object (file and filecontent), but in file it was transient and indexable and in filecontent it was persistent and non-indexable.
Also while mapping file and filecontent object using hibernate, we specified LAZY loading filecontent object.