Certain characters such as “_” and “%” are not interpreted literally because they have special meaning within databases.
Few databases like Oracle and DB2 allows the assignment of special escape characters to tell database that the character is interpreted literally.
For example consider the below table:
EMPLOYEE
--------------------------------------------
ID NAME LOCATION
--------------------------------------------
1 Raj_esh Bangalore
2 Pravin Mumbai
3 Kiran Pune
4 Rahul Chennai
From the above table if we would like to fetch all employees whose contains “_” then the query and result is:
select * from EMP_TEMP where name like '%_%'
--------------------------------------------
ID NAME LOCATION
--------------------------------------------
1 Raj_esh Bangalore
2 Pravin Mumbai
3 Kiran Pune
4 Rahul Chennai
So we didn’t get what we expected instead we got all records. As a solution to this Oracle or DB2 supports an ESCAPE clause to tell data base that the character is interpreted literally.
The modified query is
select * from EMP_TEMP where name like '%¼_%' ESCAPE ‘¼’
The character symbol ¼ is used because it can rarely occur in search criteria.
The result of the modified query is
--------------------------------------------
ID NAME LOCATION
--------------------------------------------
1 Raj_esh Bangalore
Thursday, March 11, 2010
List of vulnerabilities in any web application
1. Cross-Site Request Forgery (CSRF)
Through CSRF attacker can by-pass authentication protocols and manipulate a user’s browser into submitting valid HTTP requests to a target site.For example a user logs in to particular application and also opens another browser and navigates to some malicious site. The site can contain hidden iframe with some script that auto-posts a form back to the application.
2. Cross Site Scripting (CSS)
User attempts to embed script into the request/post parameters
3. Debug Error message enabled in production environment
Path, version and stack trace information can be gathered and used for further attacks. Attacker will be able to view SQL executed which could lead to SQL injection attacks.
4. SQL injection
Avoid the use of dynamic SQL (use setXXX() method on the prepared statement than appending it dynamically to the query)
5. Use of Java Script eval function
The attacker can inject any arbitrary code to eval function.
6. Sensitive data input fields autocomplete active
Set autocomplete=”off” to sensitive input fields. If this feature is turned on the information will be stored in plain text somewhere on the computer (in the registry, or elsewhere)
Through CSRF attacker can by-pass authentication protocols and manipulate a user’s browser into submitting valid HTTP requests to a target site.For example a user logs in to particular application and also opens another browser and navigates to some malicious site. The site can contain hidden iframe with some script that auto-posts a form back to the application.
2. Cross Site Scripting (CSS)
User attempts to embed script into the request/post parameters
3. Debug Error message enabled in production environment
Path, version and stack trace information can be gathered and used for further attacks. Attacker will be able to view SQL executed which could lead to SQL injection attacks.
4. SQL injection
Avoid the use of dynamic SQL (use setXXX() method on the prepared statement than appending it dynamically to the query)
5. Use of Java Script eval function
The attacker can inject any arbitrary code to eval function.
6. Sensitive data input fields autocomplete active
Set autocomplete=”off” to sensitive input fields. If this feature is turned on the information will be stored in plain text somewhere on the computer (in the registry, or elsewhere)
Wednesday, March 3, 2010
Escape special characters in hibernate criteria
This is the continuation of my previous post. The method mentioned in previous post will help in case of direct SQL and HQL. We will see how to implement the same thing with hibernate criteria.
We will extend the default functionality of org.hibernate.criterion.IlikeExpression and override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) method
to include ESCAPE clause at the end of sql query.
So we need to generate sql out of hibernate criteria in the flowing pattern in order to tell data base that special characters are interpreted literally..
select * from EMP_TEMP where name like '%¼_%' ESCAPE ‘¼’
Two need to do two things in order to achieve this.
1. Append escape character with search value containing special character.
2. Append ESCAPE clause at the end of the generated query.
The first step appending escape character with search value is as follows
public static String HIBERNATE_ESCAPE_CHAR = "¼";
String searchValue = “%_%”;
//In the simillar fashion we can escape “%” also
searchValue = searchValue.replaceAll(“_”,HIBERNATE_ESCAPE_CHAR + "_");
The second step is to append ESCAPE clause with sql. For that first we will create custom expression by extending IlikeExpression as specified below
public class EscapedILikeExpression extends IlikeExpression {
public static final String ESCAPE_CHAR = " ESCAPE '¼' ";
/**
* @param propertyName
* @param value
*/
public EscapedILikeExpression(String propertyName, Object value) {
super(propertyName, value);
// TODO Auto-generated constructor stub
}
/**
* @param propertyName
* @param value
* @param matchMode
*/
public EscapedILikeExpression(String propertyName, String value,MatchMode matchMode) {
super(propertyName, value, matchMode);
// TODO Auto-generated constructor stub
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
throws HibernateException {
String sql = super.toSqlString(criteria, criteriaQuery);
sql = sql + ESCAPE_CHAR;
return sql;
}
}
And add EscapedRestrictions to use EscapedIlikeExpression
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
public class EscapedRestrictions {
public static Criterion ilike(String propertyName, String value) {
return new EscapedILikeExpression(propertyName, value);
}
public static Criterion ilike(String propertyName, String value, MatchMode matchMode) {
return new EscapedILikeExpression(propertyName, value, matchMode);
}
}
Now the criteria code will look like this which makes use of EscapedRestrictions.java
Session session = hibernateTemplate.getSessionFactory().openSession();
Criteria criteria = session.createCriteria(EmpTemp.class)
.add(EscapedRestrictions.ilike("name", searchValue));
List<EmpTemp> list = criteria.list();
if(list != null && !list.isEmpty()){
for(EmpTemp employee : list){
System.out.println(employee.getName());
System.out.println(employee.getLocation ());
}
}
session.close();
This would result the SQL query same as below and provide results that we expected.
select * from EMP_TEMP where name like '%¼_%' ESCAPE ‘¼’
We will extend the default functionality of org.hibernate.criterion.IlikeExpression and override
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery) method
to include ESCAPE clause at the end of sql query.
So we need to generate sql out of hibernate criteria in the flowing pattern in order to tell data base that special characters are interpreted literally..
select * from EMP_TEMP where name like '%¼_%' ESCAPE ‘¼’
Two need to do two things in order to achieve this.
1. Append escape character with search value containing special character.
2. Append ESCAPE clause at the end of the generated query.
The first step appending escape character with search value is as follows
public static String HIBERNATE_ESCAPE_CHAR = "¼";
String searchValue = “%_%”;
//In the simillar fashion we can escape “%” also
searchValue = searchValue.replaceAll(“_”,HIBERNATE_ESCAPE_CHAR + "_");
The second step is to append ESCAPE clause with sql. For that first we will create custom expression by extending IlikeExpression as specified below
public class EscapedILikeExpression extends IlikeExpression {
public static final String ESCAPE_CHAR = " ESCAPE '¼' ";
/**
* @param propertyName
* @param value
*/
public EscapedILikeExpression(String propertyName, Object value) {
super(propertyName, value);
// TODO Auto-generated constructor stub
}
/**
* @param propertyName
* @param value
* @param matchMode
*/
public EscapedILikeExpression(String propertyName, String value,MatchMode matchMode) {
super(propertyName, value, matchMode);
// TODO Auto-generated constructor stub
}
public String toSqlString(Criteria criteria, CriteriaQuery criteriaQuery)
throws HibernateException {
String sql = super.toSqlString(criteria, criteriaQuery);
sql = sql + ESCAPE_CHAR;
return sql;
}
}
And add EscapedRestrictions to use EscapedIlikeExpression
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.MatchMode;
public class EscapedRestrictions {
public static Criterion ilike(String propertyName, String value) {
return new EscapedILikeExpression(propertyName, value);
}
public static Criterion ilike(String propertyName, String value, MatchMode matchMode) {
return new EscapedILikeExpression(propertyName, value, matchMode);
}
}
Now the criteria code will look like this which makes use of EscapedRestrictions.java
Session session = hibernateTemplate.getSessionFactory().openSession();
Criteria criteria = session.createCriteria(EmpTemp.class)
.add(EscapedRestrictions.ilike("name", searchValue));
List<EmpTemp> list = criteria.list();
if(list != null && !list.isEmpty()){
for(EmpTemp employee : list){
System.out.println(employee.getName());
System.out.println(employee.getLocation ());
}
}
session.close();
This would result the SQL query same as below and provide results that we expected.
select * from EMP_TEMP where name like '%¼_%' ESCAPE ‘¼’
Wednesday, February 24, 2010
JAX-WS RESTful web services
Publishing a Restful web service with JAX-WS starts by implementing the javax.xml.ws.Provider<T> interface. Implementations are required to support Provider<Source> and Provider<SOAPMessage>. The ServiceMode annotation can be used to control whether the Provider instance will receive entire protocol messages or just message payloads.
Here we are going to look at an example which accepts search consumer request and results the particular consumer details.
So our service will look like
public class ConsumerSearchService implements Provider<Source>{
}
The next step is to declare a @Resource annotation that will be used by the JAX-WS runtime to inject a WebServiceContext into our ConsumerSearchService instance.
public class ConsumerSearchService implements Provider<Source>{
@javax.annotation.Resource(type=Object.class)
protected static WebServiceContext wsContext;
}
Here we use JAXB for marshall and unmarshall the xml request and response objects.
So the next step is to create JAXBContext in the constructor of our service class.
public class ConsumerSearchService implements Provider<Source>{
@javax.annotation.Resource(type=Object.class)
protected static WebServiceContext wsContext;
private JAXBContext jc;
public ConsumerSearchService () {
try {
jc = JAXBContext.newInstance("consumer.rest");
} catch(JAXBException je) {
System.out.println("Exception " + je);
throw new WebServiceException("Cannot create JAXBContext", je);
}
}
}
The next step is to implement Source Provider<Source>.invoke (Source source)
public Source invoke(Source source) {
try{
MessageContext mc = wsContext.getMessageContext();
String path = (String)mc.get(MessageContext.PATH_INFO);
String method = (String)mc.get(MessageContext.HTTP_REQUEST_METHOD);
System.out.println("Got HTTP "+method+" request for "+path);
if (method.equals("POST"))
return handlePost(source, mc);
throw new WebServiceException("Unsupported method:" +method);
} catch(JAXBException je) {
throw new WebServiceException(je);
}
}
If you look at API documentation of MessageContext it says, javax.xml.ws.handler.MessageContext interface provides methods to manage a property set. MessageContext properties enable handlers in a handler chain to share processing related state.
From MessageContext.HTTP_REQUEST_METHOD we get HTTP method and we can handle all other methods(GET, PUT, DELETE) similarly as POST method handled above.
Next we will look at the implementation of handlePost (source, mc) method.
/**
* Handles HTTP POST.
*/
private Source handlePost (Source source, MessageContext mc)
throws JAXBException {
mc.put(MessageContext.HTTP_RESPONSE_CODE, 201);
Unmarshaller u = jc.createUnmarshaller();
ConsumerReq request = (ConsumerReq) u.unmarshal(source);
ConsumerRes response = searchConsumer(request);
return new JAXBSource(jc, response);
}
Deployment
We are going to look at deploying same service in two different environments. One is IBM specific where you can deploy the war file in Web sphere without any sun specific configurations. The other one if sun specific.
1. IBM Specific
@javax.xml.ws.WebServiceProvider(
serviceName="ConsumerSearchService",
portName="ConsumerSearchServicePort",
targetNamespace=http://consumer.details.com/v1)
By placing the above annotation statement on top of the web service class is enough to deploy the web service in web sphere.
2. Sun Specific
To deploy our endpoint on a servler container we need to make configuration changes in two files. One is in web.xml and other is to create or modify sun-jaxws.xml.
The following entries in web.xml for above example.
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener>
<servlet>
<servlet-name>ConsumerSearch</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ConsumerSearch</servlet-name>
<url-pattern>/ConsumerSearchService/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
Next we add a sun-jaxws.xml deployment descriptor to the WAR file. The following is an example.
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="ConsumerSearchService"
implementation="ConsumerSearchService"
binding="http://www.w3.org/2004/08/wsdl/http"
url-pattern='/ConsumerSearchService/*'/>
</endpoints>
Here we are going to look at an example which accepts search consumer request and results the particular consumer details.
So our service will look like
public class ConsumerSearchService implements Provider<Source>{
}
The next step is to declare a @Resource annotation that will be used by the JAX-WS runtime to inject a WebServiceContext into our ConsumerSearchService instance.
public class ConsumerSearchService implements Provider<Source>{
@javax.annotation.Resource(type=Object.class)
protected static WebServiceContext wsContext;
}
Here we use JAXB for marshall and unmarshall the xml request and response objects.
So the next step is to create JAXBContext in the constructor of our service class.
public class ConsumerSearchService implements Provider<Source>{
@javax.annotation.Resource(type=Object.class)
protected static WebServiceContext wsContext;
private JAXBContext jc;
public ConsumerSearchService () {
try {
jc = JAXBContext.newInstance("consumer.rest");
} catch(JAXBException je) {
System.out.println("Exception " + je);
throw new WebServiceException("Cannot create JAXBContext", je);
}
}
}
The next step is to implement Source Provider<Source>.invoke (Source source)
public Source invoke(Source source) {
try{
MessageContext mc = wsContext.getMessageContext();
String path = (String)mc.get(MessageContext.PATH_INFO);
String method = (String)mc.get(MessageContext.HTTP_REQUEST_METHOD);
System.out.println("Got HTTP "+method+" request for "+path);
if (method.equals("POST"))
return handlePost(source, mc);
throw new WebServiceException("Unsupported method:" +method);
} catch(JAXBException je) {
throw new WebServiceException(je);
}
}
If you look at API documentation of MessageContext it says, javax.xml.ws.handler.MessageContext interface provides methods to manage a property set. MessageContext properties enable handlers in a handler chain to share processing related state.
From MessageContext.HTTP_REQUEST_METHOD we get HTTP method and we can handle all other methods(GET, PUT, DELETE) similarly as POST method handled above.
Next we will look at the implementation of handlePost (source, mc) method.
/**
* Handles HTTP POST.
*/
private Source handlePost (Source source, MessageContext mc)
throws JAXBException {
mc.put(MessageContext.HTTP_RESPONSE_CODE, 201);
Unmarshaller u = jc.createUnmarshaller();
ConsumerReq request = (ConsumerReq) u.unmarshal(source);
ConsumerRes response = searchConsumer(request);
return new JAXBSource(jc, response);
}
Deployment
We are going to look at deploying same service in two different environments. One is IBM specific where you can deploy the war file in Web sphere without any sun specific configurations. The other one if sun specific.
1. IBM Specific
@javax.xml.ws.WebServiceProvider(
serviceName="ConsumerSearchService",
portName="ConsumerSearchServicePort",
targetNamespace=http://consumer.details.com/v1)
By placing the above annotation statement on top of the web service class is enough to deploy the web service in web sphere.
2. Sun Specific
To deploy our endpoint on a servler container we need to make configuration changes in two files. One is in web.xml and other is to create or modify sun-jaxws.xml.
The following entries in web.xml for above example.
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee">
<listener>
<listener-class>com.sun.xml.ws.transport.http.servlet.WSServletContextListener
</listener>
<servlet>
<servlet-name>ConsumerSearch</servlet-name>
<servlet-class>com.sun.xml.ws.transport.http.servlet.WSServlet
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>ConsumerSearch</servlet-name>
<url-pattern>/ConsumerSearchService/*</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>60</session-timeout>
</session-config>
</web-app>
Next we add a sun-jaxws.xml deployment descriptor to the WAR file. The following is an example.
<?xml version="1.0" encoding="UTF-8"?>
<endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0">
<endpoint name="ConsumerSearchService"
implementation="ConsumerSearchService"
binding="http://www.w3.org/2004/08/wsdl/http"
url-pattern='/ConsumerSearchService/*'/>
</endpoints>
Tuesday, February 23, 2010
Hibernate update and dynamic-update
Introduction:
When we start working on hibernate we always get confused with some properties and their behavior. This write up explains the difference between two such properties that are used for table update.
update="false" and dynamic-update="true"
There are two scenarios with respect to hibernate update functionality.
1. One scenario is that we never want to update some fields. We just need to retain the value that was created originally. In such cases we have to set update="false" in hibernate mapping file as mentioned below.
<property name=”xxx” column=”xxx” update=”false”>
Practical usage of this could be, for example consider the following entity
public class User{
Integer userId;
String firstName;
String lastName;
Date createDate;
String createdBy;
Date updateDate;
String updatedBy;
}
The "createDate" and "createdBy" fields should be set only for the first time when it is created. Any further updates to that row should not modify "createDate" and "createdBy" fields.
So the configuration for this is set update=”false” in user.hbm.xml file.
<property name="createDate" column="createDate" update="false">
<property name="createdBy" column="createdBy" update="false">
2. Second scenario is that when we call session.update() method it generates SQL update statement for all the fields of that entity. If we would like to have only the fields that we modify then we need to set dynamic-update="true" in hibernate mapping files.
For example:
Consider the values of a user table’s row
{
1,
"Joe",
"Tee",
16/10/2009,
"John",
17/10/2009,
"John"
}
And we are updating only the user name
User user = userDAO.get(1);
user.setFirstName("Bee");
userDAO.update(user);
And the update statement generated usually is
UPDATE USER SET first_name='Bee', last_name='Tee', update_date='17/10/2009', updated_by='John' WHERE user_id=1;
Though we modified only one field, all fields are getting populated in the update statement. Here USER table is small and the performance impact may be less, but if we consider production tables with huge number of columns this will have the biggest performance impact.
To improve the performance, the requirement here is that update statement should contain only the fields that are updated.
To do that we need to set
<class name="User" table="USER" dynamic-update="true">
Once the property is set the update statement that gets generated is
UPDATE user set first_name='Bee' WHERE user_id=1;
When we start working on hibernate we always get confused with some properties and their behavior. This write up explains the difference between two such properties that are used for table update.
update="false" and dynamic-update="true"
There are two scenarios with respect to hibernate update functionality.
1. One scenario is that we never want to update some fields. We just need to retain the value that was created originally. In such cases we have to set update="false" in hibernate mapping file as mentioned below.
<property name=”xxx” column=”xxx” update=”false”>
Practical usage of this could be, for example consider the following entity
public class User{
Integer userId;
String firstName;
String lastName;
Date createDate;
String createdBy;
Date updateDate;
String updatedBy;
}
The "createDate" and "createdBy" fields should be set only for the first time when it is created. Any further updates to that row should not modify "createDate" and "createdBy" fields.
So the configuration for this is set update=”false” in user.hbm.xml file.
<property name="createDate" column="createDate" update="false">
<property name="createdBy" column="createdBy" update="false">
2. Second scenario is that when we call session.update() method it generates SQL update statement for all the fields of that entity. If we would like to have only the fields that we modify then we need to set dynamic-update="true" in hibernate mapping files.
For example:
Consider the values of a user table’s row
{
1,
"Joe",
"Tee",
16/10/2009,
"John",
17/10/2009,
"John"
}
And we are updating only the user name
User user = userDAO.get(1);
user.setFirstName("Bee");
userDAO.update(user);
And the update statement generated usually is
UPDATE USER SET first_name='Bee', last_name='Tee', update_date='17/10/2009', updated_by='John' WHERE user_id=1;
Though we modified only one field, all fields are getting populated in the update statement. Here USER table is small and the performance impact may be less, but if we consider production tables with huge number of columns this will have the biggest performance impact.
To improve the performance, the requirement here is that update statement should contain only the fields that are updated.
To do that we need to set
<class name="User" table="USER" dynamic-update="true">
Once the property is set the update statement that gets generated is
UPDATE user set first_name='Bee' WHERE user_id=1;
Hibernate Interceptors
Introduction
This article mainly talks about hibernate interceptor and its uses. This also includes different types of hibernate interceptors and available APIs.
Sometimes we may require performing some operations before or after performing core functional logic. In such case an interceptor can be used to intercept the existing functionality to provide new features or add-ons to the application. Interceptors provide callback APIs which can be called on specific event or action.
Hibernate Interceptors
The Interceptor interface provides callbacks from the session to the application, allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded. One possible use for this is to track auditing information. For example, Interceptor can be used to set audit trail properties such as created by, created on, updated by and updated on automatically.
Types of Hibernate Interceptors
1. Application scope interceptors
2. Session scope interceptors
1. Application Scope Interceptors
An application may contain more than one database session. If the interceptor is configured in the application scope level, then it is applicable to persistent objects in all the sessions of that application. The following code configures application scope interceptor.
Configuration configuration = new Configuration();
configuration.setInterceptor(new AppScopeInterceptor());
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session1 = sessionFactory.openSession();
Session session2 = sessionFactory.openSession();
Hibernate Configuration class provides an API to set application scope interceptors. In the above example the interceptor is applicable to both “session1” and “session2”.
2. Session Scope Interceptors
Each session can have different interceptors configured to them. In that case the interceptor is applicable to persistent objects of that particular session.
Configuration configuration = new Configuration();
SessionFactory sessionFactory = configuration.buildSessionFactory();
FirstInterceptor firstInterceptor = new FirstInterceptor ();
Session session1 = sessionFactory.openSession(firstInterceptor);
SecondInterceptor secondInterceptor = new SecondInterceptor ();
Session session2 = sessionFactory.openSession(secondInterceptor);
In the above example two different interceptors (firstInterceptor, secondInterceptor) are configured to two different sessions (session1, session2). In that case “firstInterceptor” is applicable only for “session1” and the “secondInterceptor” for “session2”.
Interceptor API
1. Interceptor – interface
2. EmptyInterceptor - contains empty method implementation of interceptor interface.
We can either implement interceptor directly or extend EmptyInterceptor. It’s always better to extend EmptyInterceptor and override only methods that are required.
This interface allows application to provide greater customization to persist the objects. It even allows the code to modify the state of the persistent object. It has more than 15 different methods and so the designers of Hibernate provide the concrete EmptyInterceptor class which implements the Interceptor interface to provide default/empty method implementations. Applications can use EmptyInterceptor class instead of depending on the Interceptor interface.
Examples
1. Set audit trail properties
For example almost in every application the database tables might have some kind of audit trail fields. Ideally these fields need to be set based on who created/updated and when. There fields may present in almost all table at times. Using Hibernate Interceptor this can be set without touching the existing application code. So this helps the developer from setting these fields each and every time manually and places this code in a common place apart from application code.
For Example we have a base class which contains only the audit trail information.
Public class BaseEntity implements Serializable {
String createdBy
Date createdOn
String updatedBy
Date updatedOn
}
And we have PersonEntity extending BaseEntity.
Public Class PersonEntity extends BaseEntity {
String fname;
String lname;
String fullName;
}
To implement the interceptor, we need to implement the Interceptor interface or extend the EmptyIntercepter. Let’s extend EmptyInterceptor so we need to implement only the methods we actually need.
Public class AuditTrailInterceptor extends EmptyInterceptor {
public boolean onFlushDirty(
Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
setValue(currentState, propertyNames, "updatedBy", UserUtils.getCurrentUsername());
setValue(currentState, propertyNames, "updatedOn", new Date());
return true;
}
public boolean onSave(
Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
setValue(state, propertyNames, "createdBy", UserUtils.getCurrentUsername());
setValue(state, propertyNames, "createdOn", new Date());
return true;
}
private void setValue(
Object[] currentState, String[] propertyNames,
String propertyToSet, Object value) {
def index = propertyNames.toList().indexOf(propertyToSet)
if (index >= 0) {
currentState[index] = value
}
}
}
Here we implemented onFlushDirty() and onSave() methods because these are called for SQL updates and inserts, respectively. For example when a new PersonEntity is created onSave method is called and here we need to set “createdBy” and “createdOn” fields. Same way when an existing PersonEntity is modified onFlushDirty method is called and here we need to set “updatedBy” and “updatedOn”.
There are two ways to modify / view values of persistent objects. Notice that each method takes parameters of “state” array and “propertyNames” array. State array contains values of a persistent object where as propertyNames array contains properties of a persistent object.
For example “propertyNames” array of a PersonEntity might look like this
{
“createBy”,
“createdOn”,
“updatedBy”,
“updatedOn”,
“fname”,
“lname”,
“fullName”
}
And “state” array might look like this after creating and updating the same
{
“Tom”,
07-10-2009 14:27:00,
“Jerry”,
17-10-2009 14:30:00,
“xxxx”,
“yyyy”,
“xxxx yyyy”
}
One way of modifying the persistent objects are to traverse through these arrays and edit the appropriate value as we have done in “setValue” utility method.
The other way of accessing and modifying the persistent objects are through entity parameter. As specified below:-
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if (entity instanceof BaseEntity){
BaseEntity baseEntity = (BaseEntity)entity;
baseEntity.setCreatedBy(UserUtils.getCurrentUsername());
baseEntity.setCreatedOn(new Date());
}
return super.onSave(entity, id, state, propertyNames, types);
}
2. Set Derived properties
Any property is derived from existing properties can also be set through interceptors. For example in the above PersonEntity the fullName can be derived from fname and lname properties. Instead of setting this value in the application logic we can move it to interceptor.
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames,
Type[] types){
if (entity instanceof PersonEntity){
PersonEntity personEntity = (PersonEntity)entity;
String fullName = personEntity.getFName() + " " +
personEntity.getLName();
personEntity.setFullName(fullName);
}
return super.onSave(entity, id, state, propertyNames, types);
}
3. Maintain log information
Log information can also be recorded on save or update.
public class LoggerInterceptor extends EmptyInterceptor{
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames,
Type[] types){
System.out.println("Inserting the persistent Object " +
entity.getClass() + " with Id " + id);
return super.onSave(entity, id, state, propertyNames, types);
}
}
Same method can be used to monitor what values are getting saved. This helps the developer to debug the values that are passed to database.
Summary
This article is just an introduction to Hibernate Interceptors and includes few examples which reduces the redundant code and provides modularity from the core application logic.
This article mainly talks about hibernate interceptor and its uses. This also includes different types of hibernate interceptors and available APIs.
Sometimes we may require performing some operations before or after performing core functional logic. In such case an interceptor can be used to intercept the existing functionality to provide new features or add-ons to the application. Interceptors provide callback APIs which can be called on specific event or action.
Hibernate Interceptors
The Interceptor interface provides callbacks from the session to the application, allowing the application to inspect and/or manipulate properties of a persistent object before it is saved, updated, deleted or loaded. One possible use for this is to track auditing information. For example, Interceptor can be used to set audit trail properties such as created by, created on, updated by and updated on automatically.
Types of Hibernate Interceptors
1. Application scope interceptors
2. Session scope interceptors
1. Application Scope Interceptors
An application may contain more than one database session. If the interceptor is configured in the application scope level, then it is applicable to persistent objects in all the sessions of that application. The following code configures application scope interceptor.
Configuration configuration = new Configuration();
configuration.setInterceptor(new AppScopeInterceptor());
SessionFactory sessionFactory = configuration.buildSessionFactory();
Session session1 = sessionFactory.openSession();
Session session2 = sessionFactory.openSession();
Hibernate Configuration class provides an API to set application scope interceptors. In the above example the interceptor is applicable to both “session1” and “session2”.
2. Session Scope Interceptors
Each session can have different interceptors configured to them. In that case the interceptor is applicable to persistent objects of that particular session.
Configuration configuration = new Configuration();
SessionFactory sessionFactory = configuration.buildSessionFactory();
FirstInterceptor firstInterceptor = new FirstInterceptor ();
Session session1 = sessionFactory.openSession(firstInterceptor);
SecondInterceptor secondInterceptor = new SecondInterceptor ();
Session session2 = sessionFactory.openSession(secondInterceptor);
In the above example two different interceptors (firstInterceptor, secondInterceptor) are configured to two different sessions (session1, session2). In that case “firstInterceptor” is applicable only for “session1” and the “secondInterceptor” for “session2”.
Interceptor API
1. Interceptor – interface
2. EmptyInterceptor - contains empty method implementation of interceptor interface.
We can either implement interceptor directly or extend EmptyInterceptor. It’s always better to extend EmptyInterceptor and override only methods that are required.
This interface allows application to provide greater customization to persist the objects. It even allows the code to modify the state of the persistent object. It has more than 15 different methods and so the designers of Hibernate provide the concrete EmptyInterceptor class which implements the Interceptor interface to provide default/empty method implementations. Applications can use EmptyInterceptor class instead of depending on the Interceptor interface.
Examples
1. Set audit trail properties
For example almost in every application the database tables might have some kind of audit trail fields. Ideally these fields need to be set based on who created/updated and when. There fields may present in almost all table at times. Using Hibernate Interceptor this can be set without touching the existing application code. So this helps the developer from setting these fields each and every time manually and places this code in a common place apart from application code.
For Example we have a base class which contains only the audit trail information.
Public class BaseEntity implements Serializable {
String createdBy
Date createdOn
String updatedBy
Date updatedOn
}
And we have PersonEntity extending BaseEntity.
Public Class PersonEntity extends BaseEntity {
String fname;
String lname;
String fullName;
}
To implement the interceptor, we need to implement the Interceptor interface or extend the EmptyIntercepter. Let’s extend EmptyInterceptor so we need to implement only the methods we actually need.
Public class AuditTrailInterceptor extends EmptyInterceptor {
public boolean onFlushDirty(
Object entity, Serializable id, Object[] currentState,
Object[] previousState, String[] propertyNames, Type[] types) {
setValue(currentState, propertyNames, "updatedBy", UserUtils.getCurrentUsername());
setValue(currentState, propertyNames, "updatedOn", new Date());
return true;
}
public boolean onSave(
Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
setValue(state, propertyNames, "createdBy", UserUtils.getCurrentUsername());
setValue(state, propertyNames, "createdOn", new Date());
return true;
}
private void setValue(
Object[] currentState, String[] propertyNames,
String propertyToSet, Object value) {
def index = propertyNames.toList().indexOf(propertyToSet)
if (index >= 0) {
currentState[index] = value
}
}
}
Here we implemented onFlushDirty() and onSave() methods because these are called for SQL updates and inserts, respectively. For example when a new PersonEntity is created onSave method is called and here we need to set “createdBy” and “createdOn” fields. Same way when an existing PersonEntity is modified onFlushDirty method is called and here we need to set “updatedBy” and “updatedOn”.
There are two ways to modify / view values of persistent objects. Notice that each method takes parameters of “state” array and “propertyNames” array. State array contains values of a persistent object where as propertyNames array contains properties of a persistent object.
For example “propertyNames” array of a PersonEntity might look like this
{
“createBy”,
“createdOn”,
“updatedBy”,
“updatedOn”,
“fname”,
“lname”,
“fullName”
}
And “state” array might look like this after creating and updating the same
{
“Tom”,
07-10-2009 14:27:00,
“Jerry”,
17-10-2009 14:30:00,
“xxxx”,
“yyyy”,
“xxxx yyyy”
}
One way of modifying the persistent objects are to traverse through these arrays and edit the appropriate value as we have done in “setValue” utility method.
The other way of accessing and modifying the persistent objects are through entity parameter. As specified below:-
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
if (entity instanceof BaseEntity){
BaseEntity baseEntity = (BaseEntity)entity;
baseEntity.setCreatedBy(UserUtils.getCurrentUsername());
baseEntity.setCreatedOn(new Date());
}
return super.onSave(entity, id, state, propertyNames, types);
}
2. Set Derived properties
Any property is derived from existing properties can also be set through interceptors. For example in the above PersonEntity the fullName can be derived from fname and lname properties. Instead of setting this value in the application logic we can move it to interceptor.
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames,
Type[] types){
if (entity instanceof PersonEntity){
PersonEntity personEntity = (PersonEntity)entity;
String fullName = personEntity.getFName() + " " +
personEntity.getLName();
personEntity.setFullName(fullName);
}
return super.onSave(entity, id, state, propertyNames, types);
}
3. Maintain log information
Log information can also be recorded on save or update.
public class LoggerInterceptor extends EmptyInterceptor{
public boolean onSave(
Object entity, Serializable id, Object[] state, String[] propertyNames,
Type[] types){
System.out.println("Inserting the persistent Object " +
entity.getClass() + " with Id " + id);
return super.onSave(entity, id, state, propertyNames, types);
}
}
Same method can be used to monitor what values are getting saved. This helps the developer to debug the values that are passed to database.
Summary
This article is just an introduction to Hibernate Interceptors and includes few examples which reduces the redundant code and provides modularity from the core application logic.
Subscribe to:
Posts (Atom)