Friday, October 30, 2009

Hibernate Interceptors

Consider that we have requirement where we need to trim the values of the strings in the model Object that we are going to save . but if we write a method like trim() which we will call the method by passing a object as an argument which returns the trimmed() values.

but consider , i have defined all the variables in my model classes as private , so we cannot access them in our super class even and cannot change the values [trimming the spaces and reset the values ] , may throw IllegalModifierException,IllegalArgumentException.we need to change all the variable to public .

we may call the code like this ,

SomeObject=trim(SomeObject);
getSession().save(SomeObject);

so we will be calling the trim() method before when ever we say save(),update(),saveOrUpdate() and flush().

so we need to chanage all the values . what happens when we need to save Batch of objects. the trim() method will be called many times.

Lets move for a more generic Option , we will Use the Hibernate Interceptors.

so interceptors provide us a way to process the object before some thing is being done with that object [ may be saved , updated .... ].so how does we write an interceptor .

Consider a MyData model class which i going to save in Databasse , the code looks like this

public class MyData implements Serializable {

@Id@GeneratedValue
public int ID;

public MyData() {
// TODO Auto-generated constructor stub
}

public MyData(String name,String last) {
this.firstName=name;
this.lastName=last;
}

private String firstName;
private String lastName;

public int getID() {
return ID;
}
public void setID(int id) {
ID = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}

}


U can see the some of the variables are set to private .

Now lets write a Basic Interceptor .for this we have a inteceptor interface with some 15 methods . we will use a class "EmptyInterceptor " which already implements the "Interceptor" interface available in hiberante. the code looks like this


@SuppressWarnings("serial")
public class MyDataInterceptor extends EmptyInterceptor {

public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {

for(int i=0;i
if(state[i] instanceof String){
String obtainedValue=(String)state[i];
state[i]=obtainedValue.trim();
}
}//close of loop
return super.onSave(entity, id, state, propertyNames, types);
}

}


what i have done is i just wrote a Interceptor which extends EmptyInterceptor , i implemented a method onSave() [ we have some more methods like onUpdate(),onDelete() and onLoad() .... ] . what this method does is it just perform the code that we have written in the onSave() method before any model class is going to get saved.

consider if we save as above

getSession().save(SomeObject);

before saving the SomeObject , the code in the onSave() method gets exceuted. i have written the code that satisfies our functionality [trimming the String values].

Object entity : the object that we are going to save
Serializable id: The id represents the Serializable primary key (which in our case will bea simple string object).
The state array represents the values of the properties of the persistent object
The propertyNames array holds a list of string values, which are firstName,lastName.

now what i have done , i written a loop which iterates through the state array [ containing values that we are passing ] and trimming them and resetting them.this even works for private variables [ no exception ] .

and final thing as how to link this interceptor to the Hibernate Application ,

Interceptors are 2 types
Global Interceptor : set to SessionFactory
Session Interceptor : set to Session

we will use global since all objects values will be trimmed if they contain strings.

configuration.configure(configFile);
//configuration.setListener("save-update", new DefaultSaveOrUpdateListener());
configuration.setInterceptor(new MyDataInterceptor());
sessionFactory = configuration.buildSessionFactory();

we just need to say configuration.setInteceptor(Our Interceptor class)

and all objects values will be trimmed , with no code changes in any model classes or any additional methods . just a few changes.
Read More

Hibernate Annotation - @Embedded and @Embeddable

In this blog, I will explain you how to map a component using @Embedded and @Embeddable Annotations.
Consider the Student Object which contains columns ,
StudentID
StudentName
StudentClass
StudentStreet
StudentCity
StudentState

We can see the columns StudentStreet , city and state can be kept in one more object “Address”.where as the Student Object contains only StudentID,StudentName and StudentClass.
So now we can make these fields into an Address object , but still we need to save these values in to a student object for which the details belong.for this we need to make sure that the address object created for a student should be inserted into the student as a internal component.
The @Embedded annotation says the same thing , when ever we define an child object as a @Embedded inside a parent object, the child object is said to saved as a component

@Embedded annotation says that the object should be saved as internal component to other object .
@Embeddable annotation says that the object defined as @Embeddable will be saved as an internal component of another object.the object defined as a @Embeddable will not have any Primary key since it will be saved according to the parent object.

Now consider the Student Object

Public class Student implements Serializable {

Private int StudentID;

public Student() {

}

public Student(String name,String studentClass ,Address address) {
this.studentName=name;
this.studentClass=studentClass;
this.address=address;
}

@Id
@GeneratedValue
private int ID;

private String studentName;
private String studentClass;
private String studentAge;

public int getID() {
return ID;
}

public void setID(int id) {
ID = id;
}

public String getStudentAge() {
return studentAge;
}

public void setStudentAge(String studentAge) {
this.studentAge = studentAge;
}

public String getStudentName() {
return studentName;
}
public void setStudentName(String studentName) {
this.studentName = studentName;
}
public String getStudentClass() {
return studentClass;
}
public void setStudentClass(String studentClass) {
this.studentClass = studentClass;
}


@Embedded
private Address address;
public Address getAddress() {
return address;
}

public void setAddress(Address address) {
this.address = address;
}


}


we can see from the above the address object is defined with an @Embedded annotation to make that address object saved as a internal component of student object.
Now consider the Address class ,
@Embeddable
public class Address implements Serializable {


private String Street;
private String City;
private String State;

public String getStreet() {
return Street;
}

public void setStreet(String street) {
Street = street;
}

public String getCity() {
return City;
}

public void setCity(String city) {
City = city;
}

public String getState() {
return State;
}

public void setState(String state) {
State = state;
}

public Address() {
}

public Address(String street,String city,String state) {
this.Street=street;
this.City=city;
this.State=state;
}

}

@Embeddable annotation says that the object will be treated as a component.

All these columns will be saved in a single table in database. No 2 tables.
Read More